diff --git a/autotests/qmltests/tst_NavigationActionBar.qml b/autotests/qmltests/tst_NavigationActionBar.qml index 8e88f19d..fe0c24a0 100644 --- a/autotests/qmltests/tst_NavigationActionBar.qml +++ b/autotests/qmltests/tst_NavigationActionBar.qml @@ -1,322 +1,318 @@ /* * Copyright 2018 Alexander Stippich . */ import QtQuick 2.3 import QtTest 1.0 import "../../src/qml" FocusScope { - property bool expandedFilterView: false + Item { + id: persistentSettings + + property bool expandedFilterView: false + } function i18nc(string1,string2) { return string2 } Item { id: elisaTheme property int layoutHorizontalMargin: 8 property int layoutVerticalMargin: 6 property int defaultFontPointSize: 12 property int filterClearButtonMargin: layoutVerticalMargin property int ratingStarSize: 15 + property int navigationBarHeight: 100 + property int navigationBarFilterHeight: 44 } SystemPalette { id: myPalette colorGroup: SystemPalette.Active } - Binding { - target: navigationActionBar1 - property: "expandedFilterView" - value: expandedFilterView - } - NavigationActionBar { id: navigationActionBar1 mainTitle: 'testTitle1' secondaryTitle: 'secondaryTitle1' enableGoBack: true allowArtistNavigation: true showRating: true height: 100 } NavigationActionBar { id: navigationActionBar2 mainTitle: 'testTitle2' secondaryTitle: 'secondaryTitle2' enableGoBack: false allowArtistNavigation: false showRating: false expandedFilterView: true height: 100 y: 200 } TestCase { name: "TestNavigationActionBar" SignalSpy { id: enqueueSpy1 target: navigationActionBar1 signalName: "enqueue" } SignalSpy { id: enqueueSpy2 target: navigationActionBar2 signalName: "enqueue" } SignalSpy { id: replaceAndPlaySpy1 target: navigationActionBar1 signalName: "replaceAndPlay" } SignalSpy { id: replaceAndPlaySpy2 target: navigationActionBar2 signalName: "replaceAndPlay" } SignalSpy { id: goBackSpy1 target: navigationActionBar1 signalName: "goBack" } SignalSpy { id: goBackSpy2 target: navigationActionBar2 signalName: "goBack" } SignalSpy { id: filterViewChangedSpy1 target: navigationActionBar1 - signalName: "filterViewChanged" + signalName: "expandedFilterViewChanged" } SignalSpy { id: filterViewChangedSpy2 target: navigationActionBar2 - signalName: "filterViewChanged" + signalName: "expandedFilterViewChanged" } SignalSpy { id: showArtistSpy1 target: navigationActionBar1 signalName: "showArtist" } SignalSpy { id: showArtistSpy2 target: navigationActionBar2 signalName: "showArtist" } when: windowShown function init() { enqueueSpy1.clear(); enqueueSpy2.clear(); replaceAndPlaySpy1.clear(); replaceAndPlaySpy2.clear(); goBackSpy1.clear(); goBackSpy2.clear(); filterViewChangedSpy1.clear(); filterViewChangedSpy2.clear(); showArtistSpy1.clear(); showArtistSpy2.clear(); - expandedFilterView = false; + persistentSettings.expandedFilterView = false; } function test_goBack() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(filterViewChangedSpy1.count, 0); compare(filterViewChangedSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var goPreviousButtonItem1 = findChild(navigationActionBar1, "goPreviousButton"); verify(goPreviousButtonItem1 !== null, "valid goPreviousButton") mouseClick(goPreviousButtonItem1); compare(goBackSpy1.count, 1); var goPreviousButtonItem2 = findChild(navigationActionBar2, "goPreviousButton"); verify(goPreviousButtonItem2 !== null, "valid goPreviousButton") mouseClick(goPreviousButtonItem2); compare(goBackSpy2.count, 0); } function test_enqueue() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(filterViewChangedSpy1.count, 0); compare(filterViewChangedSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var enqueueButtonItem = findChild(navigationActionBar1, "enqueueButton"); verify(enqueueButtonItem !== null, "valid enqueueButton") mouseClick(enqueueButtonItem); compare(enqueueSpy1.count, 1); } function test_filterState() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); - compare(filterViewChangedSpy1.count, 0); + compare(filterViewChangedSpy1.count, 1); compare(filterViewChangedSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var showFilterButtonItem1 = findChild(navigationActionBar1, "showFilterButton"); verify(showFilterButtonItem1 !== null, "valid showFilterButton") mouseClick(showFilterButtonItem1); - compare(filterViewChangedSpy1.count, 1); + compare(filterViewChangedSpy1.count, 2); var signalArgument1 = filterViewChangedSpy1.signalArguments[0]; - expandedFilterView = signalArgument1[0]; - compare(expandedFilterView,true); + compare(navigationActionBar1.expandedFilterView, true); compare(navigationActionBar1.state,'expanded') var showFilterButtonItem2 = findChild(navigationActionBar2, "showFilterButton"); verify(showFilterButtonItem2 !== null, "valid showFilterButton") mouseClick(showFilterButtonItem2); compare(filterViewChangedSpy2.count, 1); var signalArgument2 = filterViewChangedSpy2.signalArguments[0]; - compare(signalArgument2[0],false) - expandedFilterView = signalArgument2[0]; - compare(expandedFilterView,false) - compare(navigationActionBar1.expandedFilterView, false) - compare(navigationActionBar1.state,'collapsed') + compare(navigationActionBar2.expandedFilterView, false) + compare(navigationActionBar2.state, 'collapsed') } function test_replaceAndPlay() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(filterViewChangedSpy1.count, 0); compare(filterViewChangedSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var replaceAndPlayButtonItem = findChild(navigationActionBar1, "replaceAndPlayButton"); verify(replaceAndPlayButtonItem !== null, "valid replaceAndPlayButton") mouseClick(replaceAndPlayButtonItem); compare(replaceAndPlaySpy1.count, 1); } function test_showArtist() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(filterViewChangedSpy1.count, 0); compare(filterViewChangedSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var showArtistButtonItem1 = findChild(navigationActionBar1, "showArtistButton"); verify(showArtistButtonItem1 !== null, "valid showArtistButton") mouseClick(showArtistButtonItem1); compare(showArtistSpy1.count, 1); var showArtistButtonItem2 = findChild(navigationActionBar2, "showArtistButton"); verify(showArtistButtonItem2 !== null, "valid showArtistButton") mouseClick(showArtistButtonItem2); compare(showArtistSpy2.count, 0); } function test_filterRating() { - expandedFilterView = true; + persistentSettings.expandedFilterView = true; wait(200); var ratingFilterItem1 = findChild(navigationActionBar1, "ratingFilter"); verify(ratingFilterItem1 !== null, "valid ratingFilter") mouseClick(ratingFilterItem1,1); compare(navigationActionBar1.filterRating, 2); mouseClick(ratingFilterItem1,1); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 4); mouseClick(ratingFilterItem1,1 + elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + 2 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 6); mouseClick(ratingFilterItem1,1 + 2 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + 3 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 8); mouseClick(ratingFilterItem1,1 + 3 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + 4 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 10); mouseClick(ratingFilterItem1,1 + 4 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); var ratingFilterItem2 = findChild(navigationActionBar2, "ratingFilter"); verify(ratingFilterItem2 !== null, "valid ratingFilter") mouseClick(ratingFilterItem2,1); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + 2 * elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + 3 * elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + 4 * elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); } function test_filterText() { var textsFilterItem1 = findChild(navigationActionBar1, "filterTextInput"); verify(textsFilterItem1 !== null, "valid filterTextInput") compare(textsFilterItem1.focus, false); mouseClick(textsFilterItem1); compare(textsFilterItem1.focus, true); keyClick(Qt.Key_T); keyClick(Qt.Key_E); keyClick(Qt.Key_S); keyClick(Qt.Key_T); compare(navigationActionBar1.filterText, 'test'); mouseClick(textsFilterItem1,textsFilterItem1.width - textsFilterItem1.height); compare(navigationActionBar1.filterText, ""); } } } diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index a67d800a..11876264 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,858 +1,792 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.elisa 1.0 RowLayout { id: contentViewContainer spacing: 0 property bool showPlaylist property alias currentViewIndex: listViews.currentIndex signal toggleSearch() function goBack() { viewManager.goBack() } ViewManager { id: viewManager onSwitchAllAlbumsView: { elisa.allAlbumsProxyModel.genreFilterText = '' listViews.currentIndex = 1 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 1 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } onSwitchOneAlbumView: { elisa.singleAlbumProxyModel.loadAlbumData(databaseId) currentStackView.push(albumView, { mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, stackView: currentStackView, }) oneAlbumViewIsLoaded() } onSwitchAllArtistsView: { elisa.allArtistsProxyModel.genreFilterText = '' listViews.currentIndex = 2 localArtistsLoader.opacity = 1 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } onSwitchOneArtistView: { elisa.singleArtistProxyModel.setArtistFilterText(mainTitle) elisa.singleArtistProxyModel.genreFilterText = '' currentStackView.push(innerAlbumView, { mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, stackView: currentStackView, }) oneArtistViewIsLoaded() } onSwitchOneArtistFromGenreView: { elisa.singleArtistProxyModel.setArtistFilterText(mainTitle) elisa.singleArtistProxyModel.genreFilterText = genreName currentStackView.push(innerAlbumView, { mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, stackView: currentStackView, }) oneArtistViewIsLoaded() } onSwitchAllTracksView: { elisa.allTracksProxyModel.genreFilterText = '' listViews.currentIndex = 3 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 1 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } onSwitchAllGenresView: { listViews.currentIndex = 4 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 1 localFilesLoader.opacity = 0 } onSwitchFilesBrowserView: { listViews.currentIndex = 5 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 1 } onSwitchAllArtistsFromGenreView: { elisa.allArtistsProxyModel.genreFilterText = genreName currentStackView.push(innerArtistView, { contentModel: elisa.allArtistsProxyModel, mainTitle: genreName, secondaryTitle: '', image: elisaTheme.artistIcon, stackView: currentStackView, }) allArtistsFromGenreViewIsLoaded() } onSwitchOffAllViews: { localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } } ViewSelector { id: listViews Layout.fillHeight: true Layout.maximumWidth: mainWindow.width * 0.11 maximumSize: mainWindow.width * 0.11 Behavior on Layout.maximumWidth { NumberAnimation { duration: 150 } } onSwitchView: if (index === 1) { viewManager.openAllAlbums() } else if (index === 2) { viewManager.openAllArtists() } else if (index === 3) { viewManager.openAllTracks() } else if (index === 4) { viewManager.openAllGenres() } else if (index === 5) { viewManager.openFilesBrowser() } else { viewManager.closeAllViews() } } 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: { viewManager.openOneAlbum(localAlbums.stackView, innerMainTitle, innerSecondaryTitle, innerImage, databaseId) } onGoBack: viewManager.goBack() - - 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: { viewManager.openOneArtist(localArtists.stackView, innerMainTitle, innerImage, 0) } onGoBack: viewManager.goBack() - - 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 } } } Loader { id: localGenresLoader active: opacity > 0 visible: opacity > 0 opacity: 0 anchors.fill: parent onLoaded: viewManager.allGenresViewIsLoaded(item.stackView) sourceComponent: MediaBrowser { id: localGenres focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: GridBrowserView { id: allGenresView focus: true showRating: false delegateDisplaySecondaryText: false contentModel: elisa.allGenresProxyModel image: elisaTheme.genresIcon mainTitle: i18nc("Title of the view of all genres", "Genres") onOpen: { viewManager.openAllArtistsFromGenre(localGenres.stackView, innerMainTitle) } onGoBack: viewManager.goBack() - - Binding { - target: allGenresView - property: 'expandedFilterView' - value: persistentSettings.expandedFilterView - } - - onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Behavior on opacity { NumberAnimation { easing.type: Easing.InOutQuad duration: 300 } } } Loader { id: localFilesLoader anchors.fill: parent active: opacity > 0 visible: opacity > 0 opacity: 0 anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } onLoaded: viewManager.filesBrowserViewIsLoaded(item) sourceComponent: FileBrowserView { id: localFiles focus: true contentModel: elisa.fileBrowserProxyModel - - Binding { - target: localFiles - 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: '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 } }, State { name: "browsingViewsNoPlaylist" when: listViews.currentIndex !== 0 && contentViewContainer.showPlaylist !== true extend: "browsingViews" PropertyChanges { target: mainContentView Layout.fillWidth: true Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width } PropertyChanges { target: playList Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } }, State { 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 } } ] 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: { viewManager.openOneAlbum(stackView, innerMainTitle, innerSecondaryTitle, innerImage, databaseId) } onGoBack: viewManager.goBack() - - Binding { - target: innerAlbumGridView - property: 'expandedFilterView' - value: persistentSettings.expandedFilterView - } - - onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Component { id: innerArtistView GridBrowserView { id: innerAlbumGridView delegateDisplaySecondaryText: false isSubPage: true onOpen: { viewManager.openOneArtist(stackView, innerMainTitle, innerImage, 0) } onGoBack: viewManager.goBack() - - Binding { - target: innerAlbumGridView - property: 'expandedFilterView' - value: persistentSettings.expandedFilterView - } - - onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Component { id: albumView ListBrowserView { id: albumGridView contentModel: elisa.singleAlbumProxyModel isSubPage: true enableSorting: false 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: albumGridView.currentIndex = index } allowArtistNavigation: true onShowArtist: { viewManager.openOneArtist(stackView, name, elisaTheme.artistIcon, 0) } onGoBack: viewManager.goBack() - - expandedFilterView: true - - Binding { - target: albumGridView - property: 'expandedFilterView' - value: persistentSettings.expandedFilterView - } - - onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml index 453aac1c..4cb25f8d 100644 --- a/src/qml/ElisaMainWindow.qml +++ b/src/qml/ElisaMainWindow.qml @@ -1,322 +1,309 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.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: contentView.showPlaylist ? 1100 : 700 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") - property var findAction: elisa.action("edit_find") - - Controls1.Action { - shortcut: findAction.shortcut - onTriggered: { - if ( persistentSettings.expandedFilterView == true) - { - persistentSettings.expandedFilterView = false - } else { - persistentSettings.expandedFilterView = true - } - } - } Controls1.Action { shortcut: goBackAction.shortcut 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 : 1100 property int height : 600 property var playListState property var audioPlayerState property double playControlItemVolume : 100.0 property bool playControlItemMuted : false property bool playControlItemRepeat : false property bool playControlItemShuffle : false property bool expandedFilterView: false property bool showPlaylist: true property bool headerBarIsMaximized: false } 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.audioPlayerState = elisa.audioControl.persistentState persistentSettings.playControlItemVolume = headerBar.playerControl.volume persistentSettings.playControlItemMuted = headerBar.playerControl.muted persistentSettings.playControlItemRepeat = headerBar.playerControl.repeat persistentSettings.playControlItemShuffle = headerBar.playerControl.shuffle persistentSettings.showPlaylist = contentView.showPlaylist persistentSettings.headerBarIsMaximized = headerBar.isMaximized } } Loader { id: mprisloader active: false sourceComponent: PlatformIntegration { id: platformInterface playListModel: elisa.mediaPlayList audioPlayerManager: elisa.audioControl player: elisa.audioPlayer headerBarManager: elisa.manageHeaderBar manageMediaPlayerControl: elisa.playerControl onRaisePlayer: { mainWindow.show() mainWindow.raise() mainWindow.requestActivate() } } } Connections { target: elisa.audioPlayer onVolumeChanged: headerBar.playerControl.volume = elisa.audioPlayer.volume onMutedChanged: headerBar.playerControl.muted = elisa.audioPlayer.muted } Connections { target: elisa.mediaPlayList onPlayListLoadFailed: { messageNotification.showNotification(i18nc("message of passive notification when playlist load failed", "Load of playlist failed"), 3000) } } PassiveNotification { id: messageNotification } Rectangle { color: myPalette.base anchors.fill: parent ColumnLayout { anchors.fill: parent spacing: 0 Item { id: headerBarParent 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: elisa.manageHeaderBar.remainingTracks album: elisa.manageHeaderBar.album title: elisa.manageHeaderBar.title artist: elisa.manageHeaderBar.artist image: elisa.manageHeaderBar.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: elisa.playerControl.skipBackwardControlEnabled playerControl.skipForwardEnabled: elisa.playerControl.skipForwardControlEnabled playerControl.playEnabled: elisa.playerControl.playControlEnabled playerControl.isPlaying: elisa.playerControl.musicPlaying playerControl.repeat: persistentSettings.playControlItemRepeat playerControl.shuffle: persistentSettings.playControlItemShuffle playerControl.onSeek: elisa.audioPlayer.seek(position) playerControl.onPlay: elisa.audioControl.playPause() playerControl.onPause: elisa.audioControl.playPause() playerControl.onPlayPrevious: elisa.mediaPlayList.skipPreviousTrack() playerControl.onPlayNext: elisa.mediaPlayList.skipNextTrack() playerControl.isMaximized: persistentSettings.headerBarIsMaximized TrackImportNotification { id: importedTracksCountNotification anchors { right: headerBar.right top: headerBar.top rightMargin: elisaTheme.layoutHorizontalMargin * 1.75 topMargin: 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 } } } ContentView { id: contentView Layout.fillHeight: true Layout.fillWidth: true showPlaylist: persistentSettings.showPlaylist } } } StateGroup { id: mainWindowState states: [ State { name: "headerBarIsMaximized" when: headerBar.isMaximized changes: [ PropertyChanges { target: mainWindow minimumHeight: 120 + elisaTheme.mediaPlayerControlHeight explicit: true }, PropertyChanges { target: headerBarParent Layout.minimumHeight: mainWindow.height Layout.maximumHeight: mainWindow.height }, PropertyChanges { target: contentView height: 0 visible: false } ] } ] } Component.onCompleted: { elisa.initialize() elisa.mediaPlayList.randomPlay = Qt.binding(function() { return headerBar.playerControl.shuffle }) elisa.mediaPlayList.repeatPlay = Qt.binding(function() { return headerBar.playerControl.repeat }) elisa.playerControl.randomOrContinuePlay = Qt.binding(function() { return headerBar.playerControl.shuffle || headerBar.playerControl.repeat}) if (persistentSettings.playListState) { elisa.mediaPlayList.persistentState = persistentSettings.playListState } if (persistentSettings.audioPlayerState) { elisa.audioControl.persistentState = persistentSettings.audioPlayerState } elisa.audioPlayer.muted = Qt.binding(function() { return headerBar.playerControl.muted }) elisa.audioPlayer.volume = Qt.binding(function() { return headerBar.playerControl.volume }) mprisloader.active = true } } diff --git a/src/qml/FileBrowserView.qml b/src/qml/FileBrowserView.qml index a632e963..2eee6623 100644 --- a/src/qml/FileBrowserView.qml +++ b/src/qml/FileBrowserView.qml @@ -1,155 +1,152 @@ /* * Copyright 2016-2018 Matthieu Gallien * Copyright 2018 Alexander Stippich * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import 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) } } } } } diff --git a/src/qml/GridBrowserView.qml b/src/qml/GridBrowserView.qml index 01260e0b..395a6be8 100644 --- a/src/qml/GridBrowserView.qml +++ b/src/qml/GridBrowserView.qml @@ -1,157 +1,154 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtQml.Models 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: gridView property bool isSubPage: false property string mainTitle property string secondaryTitle property url image property alias contentModel: contentDirectoryView.model property alias showRating: navigationBar.showRating property bool delegateDisplaySecondaryText: true property alias expandedFilterView: navigationBar.expandedFilterView property var stackView signal open(var innerMainTitle, var innerSecondaryTitle, var innerImage, var databaseId) signal goBack() - signal filterViewChanged(bool expandedFilterView) SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } ColumnLayout { anchors.fill: parent spacing: 0 NavigationActionBar { id: navigationBar mainTitle: gridView.mainTitle secondaryTitle: gridView.secondaryTitle image: gridView.image enableGoBack: isSubPage sortOrder: if (contentModel) {contentModel.sortedAscending} else true height: elisaTheme.navigationBarHeight Layout.preferredHeight: height Layout.minimumHeight: height Layout.maximumHeight: height Layout.fillWidth: true Loader { active: contentModel !== undefined sourceComponent: Binding { target: contentModel property: 'filterText' value: navigationBar.filterText } } Loader { active: contentModel sourceComponent: Binding { target: contentModel property: 'filterRating' value: navigationBar.filterRating } } onEnqueue: contentModel.enqueueToPlayList() onReplaceAndPlay:contentModel.replaceAndPlayOfPlayList() onGoBack: gridView.goBack() - onFilterViewChanged: gridView.filterViewChanged(expandedFilterView) - onSort: contentModel.sortModel(order) } Rectangle { color: myPalette.base Layout.fillHeight: true Layout.fillWidth: true clip: true GridView { id: contentDirectoryView anchors.topMargin: 20 focus: true anchors.fill: parent ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds TextMetrics { id: secondaryLabelSize text: 'example' } cellWidth: elisaTheme.gridDelegateWidth cellHeight: (delegateDisplaySecondaryText ? elisaTheme.gridDelegateHeight : elisaTheme.gridDelegateHeight - secondaryLabelSize.height) delegate: GridBrowserDelegate { width: contentDirectoryView.cellWidth height: contentDirectoryView.cellHeight focus: true isPartial: false mainText: model.display secondaryText: if (gridView.delegateDisplaySecondaryText) {model.secondaryText} else {""} imageUrl: model.imageUrl shadowForImage: if (model.shadowForImage) {model.shadowForImage} else {false} containerData: model.containerData delegateDisplaySecondaryText: gridView.delegateDisplaySecondaryText onEnqueue: elisa.mediaPlayList.enqueue(data) onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) onOpen: gridView.open(model.display, model.secondaryText, model.imageUrl, model.databaseId) onSelected: { forceActiveFocus() contentDirectoryView.currentIndex = model.index } } } } } } diff --git a/src/qml/ListBrowserView.qml b/src/qml/ListBrowserView.qml index 6055642f..a9682ec4 100644 --- a/src/qml/ListBrowserView.qml +++ b/src/qml/ListBrowserView.qml @@ -1,120 +1,117 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.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 alias currentIndex: contentDirectoryView.currentIndex property alias enableSorting: navigationBar.enableSorting 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/NavigationActionBar.qml b/src/qml/NavigationActionBar.qml index 233b453e..7ec7635b 100644 --- a/src/qml/NavigationActionBar.qml +++ b/src/qml/NavigationActionBar.qml @@ -1,399 +1,407 @@ /* * Copyright 2016 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQml 2.2 import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.2 import QtQuick.Controls 1.4 as Controls1 FocusScope { id: navigationBar property string mainTitle property string secondaryTitle property url image property bool allowArtistNavigation: false property string labelText property bool showRating: true property alias filterText: filterTextInput.text property alias filterRating: ratingFilter.starRating property bool enableGoBack: true - property bool expandedFilterView: false + property bool expandedFilterView: persistentSettings.expandedFilterView property bool enableSorting: true property bool sortOrder signal enqueue(); signal replaceAndPlay(); signal goBack(); signal showArtist(); - signal filterViewChanged(bool expandedFilterView); signal sort(var order); Controls1.Action { id: goPreviousAction text: i18nc("navigate back in the views stack", "Back") iconName: (Qt.application.layoutDirection == Qt.RightToLeft) ? "go-next" : "go-previous" onTriggered: goBack() } + property var findAction: elisa.action("edit_find") + Controls1.Action { id: showFilterAction + shortcut: findAction.shortcut text: !navigationBar.expandedFilterView ? i18nc("Show filters in the navigation bar", "Show Search Options") : i18nc("Hide filters in the navigation bar", "Hide Search Options") iconName: !navigationBar.expandedFilterView ? "go-down-search" : "go-up-search" - onTriggered: filterViewChanged(!navigationBar.expandedFilterView) + onTriggered: { + persistentSettings.expandedFilterView = !persistentSettings.expandedFilterView + expandedFilterView = persistentSettings.expandedFilterView + if (expandedFilterView) { + filterTextInput.forceActiveFocus() + } + } } Controls1.Action { id: sortAction text: i18nc("Toggle between ascending and descending order", "Toggle sort order") iconName: sortOrder ? "view-sort-ascending" : "view-sort-descending" onTriggered: sortOrder ? sort(Qt.DescendingOrder) : sort(Qt.AscendingOrder) } ColumnLayout { anchors.fill: parent spacing: 0 anchors.topMargin: elisaTheme.layoutVerticalMargin anchors.bottomMargin: elisaTheme.layoutVerticalMargin RowLayout { spacing: 0 Layout.alignment: Qt.AlignTop Layout.preferredHeight: elisaTheme.navigationBarHeight Layout.minimumHeight: elisaTheme.navigationBarHeight Layout.maximumHeight: elisaTheme.navigationBarHeight Controls1.ToolButton { action: goPreviousAction objectName: 'goPreviousButton' Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 visible: enableGoBack } Image { id: mainIcon source: image asynchronous: true sourceSize.height: elisaTheme.coverImageSize / 2 sourceSize.width: elisaTheme.coverImageSize / 2 fillMode: Image.PreserveAspectFit Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.preferredHeight: elisaTheme.coverImageSize / 2 Layout.minimumHeight: elisaTheme.coverImageSize / 2 Layout.maximumHeight: elisaTheme.coverImageSize / 2 Layout.preferredWidth: elisaTheme.coverImageSize / 2 Layout.minimumWidth: elisaTheme.coverImageSize / 2 Layout.maximumWidth: elisaTheme.coverImageSize / 2 Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 } ColumnLayout { Layout.preferredHeight: elisaTheme.coverImageSize / 1.9 Layout.minimumHeight: elisaTheme.coverImageSize / 1.9 Layout.maximumHeight: elisaTheme.coverImageSize / 1.9 spacing: 0 Layout.fillWidth: true Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 TextMetrics { id: albumTextSize text: albumLabel.text font.pointSize: albumLabel.font.pointSize font.bold: albumLabel.font.bold } LabelWithToolTip { id: albumLabel text: mainTitle Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignLeft Layout.topMargin: secondaryTitle !== "" ? 0 : 9 elide: Text.ElideRight fontSizeMode: Text.Fit Layout.preferredHeight: elisaTheme.coverImageSize / 5 Layout.minimumHeight: elisaTheme.coverImageSize / 5 Layout.maximumHeight: elisaTheme.coverImageSize / 5 color: myPalette.text font { pointSize: elisaTheme.defaultFontPointSize * 2 } } TextMetrics { id: authorTextSize text: authorLabel.text font.pointSize: authorLabel.font.pointSize font.bold: authorLabel.font.bold } LabelWithToolTip { id: authorLabel text: secondaryTitle color: myPalette.text Layout.fillWidth: true Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter font { pointSize: elisaTheme.defaultFontPointSize } elide: Text.ElideRight visible: secondaryTitle !== "" } RowLayout { Layout.fillWidth: true spacing: 0 Layout.bottomMargin: secondaryTitle !== "" ? 0 : 14 Controls1.Button { objectName: 'enqueueButton' text: i18nc("Add current list to playlist", "Enqueue") iconName: "media-track-add-amarok" onClicked: enqueue() Layout.leftMargin: 0 Layout.rightMargin: 0 } Controls1.Button { objectName: 'replaceAndPlayButton' text: i18nc("Clear playlist and play", "Replace and Play") tooltip: i18nc("Clear playlist and add current list to it", "Replace PlayList and Play Now") iconName: "media-playback-start" onClicked: replaceAndPlay() Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 } Controls1.Button { objectName: 'showArtistButton' id: showArtistButton visible: allowArtistNavigation text: i18nc("Button to navigate to the artist of the album", "Display Artist") iconName: "view-media-artist" onClicked: showArtist() Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 } Item { Layout.fillWidth: true } Controls1.ToolButton { action: showFilterAction objectName: 'showFilterButton' Layout.alignment: Qt.AlignRight Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 } } } } RowLayout { id: filterRow spacing: 0 visible: opacity > 0.0 opacity: 0 Layout.preferredHeight: elisaTheme.navigationBarFilterHeight Layout.minimumHeight: elisaTheme.navigationBarFilterHeight Layout.maximumHeight: elisaTheme.navigationBarFilterHeight Layout.fillWidth: true Layout.topMargin: elisaTheme.layoutVerticalMargin * 2 Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.alignment: Qt.AlignTop LabelWithToolTip { text: i18nc("before the TextField input of the filter", "Search: ") font.bold: true Layout.bottomMargin: 0 color: myPalette.text } TextField { id: filterTextInput objectName: 'filterTextInput' horizontalAlignment: TextInput.AlignLeft placeholderText: i18nc("Placeholder text in the filter text box", "Album name, artist, etc.") Layout.bottomMargin: 0 Layout.fillWidth: true Layout.minimumWidth: placeHolderTextWidth.width * 1.2 implicitWidth: placeHolderTextWidth.width * 1.2 TextMetrics { id: placeHolderTextWidth text: filterTextInput.placeholderText } Image { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: elisaTheme.filterClearButtonMargin id: clearText fillMode: Image.PreserveAspectFit smooth: true visible: parent.text source: Qt.resolvedUrl(elisaTheme.clearIcon) height: parent.height width: parent.height sourceSize.width: parent.height sourceSize.height: parent.height mirror: LayoutMirroring.enabled MouseArea { id: clear anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } height: parent.parent.height width: parent.parent.height onClicked: { parent.parent.text = "" parent.parent.forceActiveFocus() } } } } LabelWithToolTip { text: i18nc("before the Rating widget input of the filter", "Rating: ") visible: showRating font.bold: true color: myPalette.text Layout.bottomMargin: 0 Layout.leftMargin: !LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 Layout.rightMargin: LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 } RatingStar { id: ratingFilter objectName: 'ratingFilter' visible: showRating hoverWidgetOpacity: 1 readOnly: false starSize: elisaTheme.ratingStarSize Layout.bottomMargin: 0 } Item { Layout.fillWidth: true implicitWidth: elisaTheme.layoutHorizontalMargin * 4 } Controls1.ToolButton { action: sortAction objectName: 'sortAscendingButton' Layout.alignment: Qt.AlignRight Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 visible: enableSorting } } } states: [ State { name: 'collapsed' when: !expandedFilterView PropertyChanges { target: navigationBar height: elisaTheme.navigationBarHeight + elisaTheme.layoutVerticalMargin * 2 } PropertyChanges { target: filterRow opacity: 0.0 } }, State { name: 'expanded' when: expandedFilterView PropertyChanges { target: navigationBar height: elisaTheme.navigationBarHeight + elisaTheme.navigationBarFilterHeight + elisaTheme.layoutVerticalMargin * 4 } PropertyChanges { target: filterRow opacity: 1.0 } } ] transitions: Transition { from: "expanded,collapsed" PropertyAnimation { properties: "height" easing.type: Easing.Linear duration: 250 } PropertyAnimation { properties: "opacity" easing.type: Easing.Linear duration: 250 } } }