diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index 94b73735..e8690087 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,830 +1,830 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.elisa 1.0 RowLayout { id: contentViewContainer spacing: 0 signal toggleSearch() function goBack() { 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.preferredWidth: mainWindow.width * 0.11 Layout.maximumWidth: mainWindow.width * 0.11 onSwitchView: if (index === 1) { viewManager.openAllAlbums() } else if (index === 2) { viewManager.openAllArtists() } else if (index === 3) { viewManager.openAllTracks() } 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: '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 delegate: MediaAlbumTrackDelegate { id: entry width: albumGridView.delegateWidth height: ((model.isFirstTrackOfDisc && !isSingleDiscAlbum) ? elisaTheme.delegateHeight*2 : elisaTheme.delegateHeight) focus: true mediaTrack.trackData: model.containerData mediaTrack.isFirstTrackOfDisc: model.isFirstTrackOfDisc mediaTrack.isSingleDiscAlbum: model.isSingleDiscAlbum mediaTrack.onEnqueue: elisa.mediaPlayList.enqueue(data) mediaTrack.onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) mediaTrack.isAlternateColor: (index % 2) === 1 - mediaTrack.onClicked: contentDirectoryView.currentIndex = index + 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/ListBrowserView.qml b/src/qml/ListBrowserView.qml index 1e675fed..18edb365 100644 --- a/src/qml/ListBrowserView.qml +++ b/src/qml/ListBrowserView.qml @@ -1,120 +1,121 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: listView property bool isSubPage: false property alias mainTitle: navigationBar.mainTitle property alias secondaryTitle: navigationBar.secondaryTitle property alias image: navigationBar.image property alias delegate: contentDirectoryView.delegate property alias contentModel: contentDirectoryView.model property alias expandedFilterView: navigationBar.expandedFilterView property alias showRating: navigationBar.showRating property alias allowArtistNavigation: navigationBar.allowArtistNavigation property var delegateWidth: scrollBar.visible ? contentDirectoryView.width - scrollBar.width : contentDirectoryView.width + property alias currentIndex: contentDirectoryView.currentIndex 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/MediaTrackDelegate.qml b/src/qml/MediaTrackDelegate.qml index 94dfc352..4dc60619 100644 --- a/src/qml/MediaTrackDelegate.qml +++ b/src/qml/MediaTrackDelegate.qml @@ -1,417 +1,417 @@ /* * Copyright 2016-2017 Matthieu Gallien * Copyright 2017 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 import QtQuick.Controls 1.4 as Controls1 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: mediaTrack property alias trackData: dataHelper.trackData property alias discNumber: dataHelper.discNumber property bool isFirstTrackOfDisc property bool isSingleDiscAlbum property bool isAlternateColor property bool detailedView: true signal clicked() signal enqueue(var data) signal replaceAndPlay(var data) Controls1.Action { id: replaceAndPlayAction text: i18nc("Clear play list and enqueue current track", "Play Now and Replace Play List") iconName: "media-playback-start" onTriggered: replaceAndPlay(dataHelper.databaseId) } Controls1.Action { id: enqueueAction text: i18nc("Enqueue current track", "Enqueue") iconName: "media-track-add-amarok" onTriggered: enqueue(dataHelper.databaseId) } Controls1.Action { id: viewDetailsAction text: i18nc("Show track metadata", "View Details") iconName: "help-about" onTriggered: { if (metadataLoader.active === false) { metadataLoader.active = true } else { metadataLoader.item.close(); metadataLoader.active = false } } } TrackDataHelper { id: dataHelper } Keys.onReturnPressed: enqueueToPlaylist(trackData) Keys.onEnterPressed: enqueueToPlaylist(trackData) Loader { id: metadataLoader active: false onLoaded: item.show() sourceComponent: MediaTrackMetadataView { trackDataHelper: dataHelper onRejected: metadataLoader.active = false; } } Rectangle { id: rowRoot anchors.fill: parent color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) MouseArea { id: hoverArea anchors.fill: parent hoverEnabled: true focus: true acceptedButtons: Qt.LeftButton onClicked: { hoverArea.forceActiveFocus() mediaTrack.clicked() } onDoubleClicked: enqueue(trackData) RowLayout { anchors.fill: parent spacing: 0 LabelWithToolTip { id: mainLabel visible: !detailedView text: { - if (dataHelper.hasValidTrackNumber()) { + if (dataHelper.hasValidTrackNumber) { if (artist !== albumArtist) return i18nc("%1: track number. %2: track title. %3: artist name", "%1 - %2 - %3", - Number(dataHelper.trackNumber).toLocaleString(Qt.locale(), 'f', 0), + dataHelper.trackNumber.toLocaleString(Qt.locale(), 'f', 0), dataHelper.title, dataHelper.artist); else return i18nc("%1: track number. %2: track title.", "%1 - %2", - Number(dataHelper.trackNumber).toLocaleString(Qt.locale(), 'f', 0), + dataHelper.trackNumber.toLocaleString(Qt.locale(), 'f', 0), dataHelper.title); } else { if (dataHelper.artist !== dataHelper.albumArtist) return i18nc("%1: track title. %2: artist name", "%1 - %2", dataHelper.title, dataHelper.artist); else return i18nc("%1: track title", "%1", dataHelper.title); } } elide: Text.ElideRight horizontalAlignment: Text.AlignLeft color: myPalette.text Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.fillWidth: true Layout.leftMargin: { if (!LayoutMirroring.enabled) return (!isSingleDiscAlbum ? elisaTheme.layoutHorizontalMargin * 4 : elisaTheme.layoutHorizontalMargin) else return 0 } Layout.rightMargin: { if (LayoutMirroring.enabled) return (!isSingleDiscAlbum ? elisaTheme.layoutHorizontalMargin * 4 : elisaTheme.layoutHorizontalMargin) else return 0 } } Item { Layout.preferredHeight: mediaTrack.height * 0.9 Layout.preferredWidth: mediaTrack.height * 0.9 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter visible: detailedView Image { id: coverImageElement anchors.fill: parent sourceSize.width: mediaTrack.height * 0.9 sourceSize.height: mediaTrack.height * 0.9 fillMode: Image.PreserveAspectFit smooth: true - source: (dataHelper.hasValidAlbumCover() ? dataHelper.albumCover : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) + source: (dataHelper.hasValidAlbumCover ? dataHelper.albumCover : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) asynchronous: true - layer.enabled: dataHelper.hasValidAlbumCover() ? true : false + layer.enabled: dataHelper.hasValidAlbumCover ? true : false layer.effect: DropShadow { source: coverImageElement radius: 10 spread: 0.1 samples: 21 color: myPalette.shadow } } } ColumnLayout { visible: detailedView Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignLeft spacing: 0 LabelWithToolTip { id: mainLabelDetailed text: { - if (dataHelper.hasValidTrackNumber()) { + if (dataHelper.hasValidTrackNumber) { return i18nc("%1: track number. %2: track title", "%1 - %2", - Number(dataHelper.trackNumber).toLocaleString(Qt.locale(), 'f', 0), dataHelper.title); + dataHelper.trackNumber.toLocaleString(Qt.locale(), 'f', 0), dataHelper.title); } else { return dataHelper.title; } } horizontalAlignment: Text.AlignLeft font.weight: Font.Bold color: myPalette.text Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.fillWidth: true Layout.topMargin: elisaTheme.layoutVerticalMargin / 2 elide: Text.ElideRight } Item { Layout.fillHeight: true } LabelWithToolTip { id: artistLabel text: (isSingleDiscAlbum ? dataHelper.artist + ' - ' + dataHelper.albumName : dataHelper.artist + ' - ' + dataHelper.albumName + ' - CD ' + dataHelper.discNumber) horizontalAlignment: Text.AlignLeft font.weight: Font.Light font.italic: true color: myPalette.text Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.fillWidth: true Layout.bottomMargin: elisaTheme.layoutVerticalMargin / 2 elide: Text.ElideRight } } Loader { id: hoverLoader active: false Layout.alignment: Qt.AlignVCenter | Qt.AlignRight z: 1 opacity: 0 sourceComponent: Row { anchors.centerIn: parent Controls1.ToolButton { id: detailsButton height: elisaTheme.delegateHeight width: elisaTheme.delegateHeight action: viewDetailsAction } Controls1.ToolButton { id: enqueueButton height: elisaTheme.delegateHeight width: elisaTheme.delegateHeight action: enqueueAction } Controls1.ToolButton { id: clearAndEnqueueButton scale: LayoutMirroring.enabled ? -1 : 1 height: elisaTheme.delegateHeight width: elisaTheme.delegateHeight action: replaceAndPlayAction } } } RatingStar { id: ratingWidget starSize: elisaTheme.ratingStarSize starRating: dataHelper.rating Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin } TextMetrics { id: durationTextMetrics text: i18nc("This is used to preserve a fixed width for the duration text.", "00:00") } LabelWithToolTip { id: durationLabel text: dataHelper.duration font.weight: Font.Light color: myPalette.text horizontalAlignment: Text.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.preferredWidth: durationTextMetrics.width + 1 } } } } states: [ State { name: 'notSelected' when: !hoverArea.containsMouse && !mediaTrack.activeFocus PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } PropertyChanges { target: rowRoot color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) } }, State { name: 'hoveredOrSelected' when: hoverArea.containsMouse || mediaTrack.activeFocus PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: hoverLoader opacity: 1.0 } PropertyChanges { target: rowRoot color: myPalette.mid } } ] transitions: [ Transition { to: 'hoveredOrSelected' SequentialAnimation { PropertyAction { properties: "active" } ParallelAnimation { NumberAnimation { properties: "opacity" easing.type: Easing.InOutQuad duration: 250 } ColorAnimation { properties: "color" duration: 250 } } } }, Transition { to: 'notSelected' SequentialAnimation { ParallelAnimation { NumberAnimation { properties: "opacity" easing.type: Easing.InOutQuad duration: 250 } ColorAnimation { properties: "color" duration: 250 } } PropertyAction { properties: "active" } } } ] } diff --git a/src/qml/MediaTrackMetadataView.qml b/src/qml/MediaTrackMetadataView.qml index 5b6c39c9..9419653f 100644 --- a/src/qml/MediaTrackMetadataView.qml +++ b/src/qml/MediaTrackMetadataView.qml @@ -1,234 +1,236 @@ /* * Copyright 2017 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 Window { id: trackMetadata property var trackDataHelper signal rejected() LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true title: i18nc("Window title for track metadata", "View Details") function displayDataAndResize() { trackList.clear() if (trackDataHelper.hasValidTitle) trackList.append({"name": i18nc("Track title for track metadata view", "Title:"), "content": trackDataHelper.title}) if (trackDataHelper.hasValidArtist) trackList.append({"name": i18nc("Track artist for track metadata view", "Artist:"), "content": trackDataHelper.artist}) if (trackDataHelper.hasValidAlbumName) trackList.append({"name": i18nc("Album name for track metadata view", "Album:"), "content": trackDataHelper.albumName}) if (trackDataHelper.hasValidTrackNumber) trackList.append({"name": i18nc("Track number for track metadata view", "Track Number:"), "content": trackDataHelper.trackNumber.toString()}) if (trackDataHelper.hasValidDiscNumber) trackList.append({"name": i18nc("Disc number for track metadata view", "Disc Number:"), "content": trackDataHelper.discNumber.toString()}) if (trackDataHelper.hasValidAlbumArtist) trackList.append({"name": i18nc("Album artist for track metadata view", "Album Artist:"), "content": trackDataHelper.albumArtist}) trackList.append({"name": i18nc("Duration label for track metadata view", "Duration:"), "content": trackDataHelper.duration}) if (trackDataHelper.hasValidYear) trackList.append({"name": i18nc("Year label for track metadata view", "Year:"), "content": trackDataHelper.year.toString()}) if (trackDataHelper.hasValidGenre) trackList.append({"name": i18nc("Genre label for track metadata view", "Genre:"), "content": trackDataHelper.genre}) if (trackDataHelper.hasValidComposer) trackList.append({"name": i18nc("Composer name for track metadata view", "Composer:"), "content": trackDataHelper.composer}) if (trackDataHelper.hasValidLyricist) trackList.append({"name": i18nc("Lyricist label for track metadata view", "Lyricist:"), "content": trackDataHelper.lyricist}) if (trackDataHelper.hasValidBitRate) trackList.append({"name": i18nc("Bit rate label for track metadata view", "Bit Rate:"), "content": (trackDataHelper.bitRate / 1000) + " " + i18nc("Unit of the bit rate in thousand", "kbit/s")}) if (trackDataHelper.hasValidSampleRate) trackList.append({"name": i18nc("Sample Rate label for track metadata view", "Sample Rate:"), "content": trackDataHelper.sampleRate + " " + i18nc("Unit of the sample rate", "Hz")}) if (trackDataHelper.hasValidChannels) trackList.append({"name": i18nc("Channels label for track metadata view", "Channels:"), "content": trackDataHelper.channels.toString()}) if (trackDataHelper.hasValidComment) trackList.append({"name": i18nc("Comment label for track metadata view", "Comment:"), "content": trackDataHelper.comment}) trackData.Layout.preferredHeight = textSize.height * trackData.count trackMetadata.height = textSize.height * (trackData.count + 1 + ( trackDataHelper.hasValidRating ? 1 : 0 )) + buttons.height + elisaTheme.layoutHorizontalMargin trackMetadata.minimumHeight = trackMetadata.height + trackMetadata.maximumHeight = trackMetadata.height trackMetadata.width = elisaTheme.trackMetadataWidth + (trackDataHelper.hasValidAlbumCover ? elisaTheme.coverImageSize : 0) trackMetadata.minimumWidth = trackMetadata.width + trackMetadata.maximumWidth = trackMetadata.width } Connections { target: trackDataHelper onTrackDataChanged: displayDataAndResize() } modality: Qt.NonModal - flags: Qt.Window | Qt.WindowCloseButtonHint + flags: Qt.Dialog | Qt.WindowCloseButtonHint Component.onCompleted: displayDataAndResize() Rectangle { anchors.fill: parent color: myPalette.window ColumnLayout { anchors.fill: parent spacing: 0 RowLayout { Layout.fillHeight: true Layout.fillWidth: true spacing: 0 Image { source: trackDataHelper.albumCover visible: trackDataHelper.hasValidAlbumCover width: elisaTheme.coverImageSize height: elisaTheme.coverImageSize fillMode: Image.PreserveAspectFit Layout.preferredHeight: height Layout.preferredWidth: width Layout.minimumHeight: height Layout.minimumWidth: width Layout.maximumHeight: height Layout.maximumWidth: width } ColumnLayout { Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.fillHeight: true Layout.preferredWidth: elisaTheme.trackMetadataWidth spacing: 0 ListView { id: trackData interactive: false Layout.fillWidth: true model: ListModel { id: trackList } delegate: RowLayout { id: delegateRow spacing: 0 LabelWithToolTip { text: name color: myPalette.text horizontalAlignment: Text.AlignRight Layout.preferredWidth: trackData.width * 0.3 Layout.minimumHeight: textSize.height Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 } LabelWithToolTip { text: content color: myPalette.text horizontalAlignment: Text.AlignLeft elide: Text.ElideRight Layout.preferredWidth: trackData.width * 0.66 Layout.fillWidth: true Layout.minimumHeight: textSize.height } } } RowLayout { anchors.margins: 0 spacing: 0 visible: trackDataHelper.hasValidRating Layout.minimumHeight: textSize.height LabelWithToolTip { id: ratingText text: i18nc("Rating label for information panel", "Rating:") color: myPalette.text horizontalAlignment: Text.AlignRight Layout.preferredWidth: trackData.width * 0.3 Layout.rightMargin: elisaTheme.layoutHorizontalMargin } TextMetrics { id: textSize font: ratingText.font text: ratingText.text } RatingStar { id: ratingWidget starRating: trackDataHelper.rating readOnly: true starSize: elisaTheme.ratingStarSize Layout.fillWidth: true } ColorOverlay { source: ratingWidget z: 2 anchors.fill: ratingWidget color: myPalette.text } } } } LabelWithToolTip { id: trackResource text: trackDataHelper.resourceURI color: myPalette.text font.italic: true elide: Text.ElideLeft horizontalAlignment: Text.AlignRight Layout.minimumHeight: textSize.height Layout.preferredWidth: trackDataHelper.hasValidAlbumCover ? elisaTheme.coverImageSize + elisaTheme.trackMetadataWidth : elisaTheme.trackMetadataWidth + 2 * elisaTheme.layoutHorizontalMargin Layout.fillWidth: true Layout.topMargin: elisaTheme.layoutVerticalMargin } DialogButtonBox { id: buttons standardButtons: DialogButtonBox.Close alignment: Qt.AlignRight onRejected: trackMetadata.rejected() Layout.fillWidth: true } } } } diff --git a/src/qml/PlayListEntry.qml b/src/qml/PlayListEntry.qml index f0ffc17a..53e7157a 100644 --- a/src/qml/PlayListEntry.qml +++ b/src/qml/PlayListEntry.qml @@ -1,454 +1,455 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 import QtQuick.Controls 1.4 as Controls1 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: playListEntry property var index property bool isSingleDiscAlbum property int isPlaying property bool isSelected property bool isValid property bool isAlternateColor property bool containsMouse property bool hasAlbumHeader property string titleDisplay property alias trackData: dataHelper.trackData signal startPlayback() signal pausePlayback() signal removeFromPlaylist(var trackIndex) signal switchToTrack(var trackIndex) height: (hasAlbumHeader ? elisaTheme.delegateWithHeaderHeight : elisaTheme.delegateHeight) Controls1.Action { id: removeFromPlayList text: i18nc("Remove current track from play list", "Remove") iconName: "error" onTriggered: { playListEntry.removeFromPlaylist(playListEntry.index) } } Controls1.Action { id: playNow text: i18nc("Play now current track from play list", "Play Now") iconName: "media-playback-start" enabled: !(isPlaying === MediaPlayList.IsPlaying) && isValid onTriggered: { if (isPlaying === MediaPlayList.NotPlaying) { playListEntry.switchToTrack(playListEntry.index) } playListEntry.startPlayback() } } Controls1.Action { id: pauseNow text: i18nc("Pause current track from play list", "Pause") iconName: "media-playback-pause" enabled: isPlaying == MediaPlayList.IsPlaying && isValid onTriggered: playListEntry.pausePlayback() } Controls1.Action { id: showInfo text: i18nc("Show track metadata", "View Details") iconName: "help-about" enabled: isValid onTriggered: { if (metadataLoader.active === false) { metadataLoader.active = true } else { metadataLoader.item.close(); metadataLoader.active = false } } } TrackDataHelper { id: dataHelper } Loader { id: metadataLoader active: false onLoaded: item.show() sourceComponent: MediaTrackMetadataView { trackDataHelper: dataHelper onRejected: metadataLoader.active = false; } } Rectangle { id: entryBackground anchors.fill: parent color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) height: (hasAlbumHeader ? elisaTheme.delegateWithHeaderHeight : elisaTheme.delegateHeight) focus: true ColumnLayout { spacing: 0 anchors.fill: parent anchors.leftMargin: elisaTheme.layoutHorizontalMargin anchors.rightMargin: elisaTheme.layoutHorizontalMargin Item { Layout.fillWidth: true Layout.preferredHeight: elisaTheme.delegateWithHeaderHeight - elisaTheme.delegateHeight Layout.minimumHeight: elisaTheme.delegateWithHeaderHeight - elisaTheme.delegateHeight Layout.maximumHeight: elisaTheme.delegateWithHeaderHeight - elisaTheme.delegateHeight visible: hasAlbumHeader RowLayout { id: headerRow spacing: elisaTheme.layoutHorizontalMargin anchors.fill: parent Image { id: mainIcon source: (isValid ? (dataHelper.hasValidAlbumCover ? dataHelper.albumCover : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) : Qt.resolvedUrl(elisaTheme.errorIcon)) Layout.minimumWidth: headerRow.height - 4 Layout.maximumWidth: headerRow.height - 4 Layout.preferredWidth: headerRow.height - 4 Layout.minimumHeight: headerRow.height - 4 Layout.maximumHeight: headerRow.height - 4 Layout.preferredHeight: headerRow.height - 4 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter sourceSize.width: headerRow.height - 4 sourceSize.height: parent.height - 4 fillMode: Image.PreserveAspectFit asynchronous: true visible: isValid } BrightnessContrast { source: mainIcon cached: true visible: !isValid contrast: -0.9 Layout.minimumWidth: headerRow.height - 4 Layout.maximumWidth: headerRow.height - 4 Layout.preferredWidth: headerRow.height - 4 Layout.minimumHeight: headerRow.height - 4 Layout.maximumHeight: headerRow.height - 4 Layout.preferredHeight: headerRow.height - 4 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter } ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true spacing: 0 LabelWithToolTip { id: mainLabel text: dataHelper.albumName font.weight: Font.Bold color: myPalette.text horizontalAlignment: Text.AlignHCenter Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.topMargin: elisaTheme.layoutVerticalMargin elide: Text.ElideRight } Item { Layout.fillHeight: true } LabelWithToolTip { id: authorLabel text: dataHelper.albumArtist font.weight: Font.Light color: myPalette.text horizontalAlignment: Text.AlignHCenter Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.bottomMargin: elisaTheme.layoutVerticalMargin elide: Text.ElideRight } } } } Item { Layout.fillWidth: true Layout.fillHeight: true RowLayout { id: trackRow anchors.fill: parent spacing: elisaTheme.layoutHorizontalMargin LabelWithToolTip { id: mainCompactLabel text: { if (dataHelper.hasValidTrackNumber) { if (dataHelper.hasValidDiscNumber && !isSingleDiscAlbum) return i18nc("%1: disk number. %2: track number. %3: track title", "%1 - %2 - %3", Number(dataHelper.discNumber).toLocaleString(Qt.locale(), 'f', 0), Number(dataHelper.trackNumber).toLocaleString(Qt.locale(), 'f', 0), dataHelper.title); else return i18nc("%1: track number. %2: track title", "%1 - %2", Number(dataHelper.trackNumber).toLocaleString(Qt.locale(), 'f', 0), dataHelper.title); } else { return dataHelper.title; } } font.weight: (isPlaying ? Font.Bold : Font.Normal) color: myPalette.text Layout.maximumWidth: mainCompactLabel.implicitWidth + 1 Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft visible: isValid elide: Text.ElideRight horizontalAlignment: Text.AlignLeft } LabelWithToolTip { id: mainInvalidCompactLabel text: titleDisplay font.weight: Font.Normal color: myPalette.text Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft visible: !isValid elide: Text.ElideRight } Item { Layout.fillWidth: true Layout.preferredWidth: 0 } Controls1.ToolButton { id: infoButton objectName: 'infoButton' implicitHeight: elisaTheme.smallDelegateToolButtonSize implicitWidth: elisaTheme.smallDelegateToolButtonSize opacity: 0 visible: opacity > 0.1 action: showInfo Layout.alignment: Qt.AlignVCenter | Qt.AlignRight } Controls1.ToolButton { id: playPauseButton objectName: 'playPauseButton' implicitHeight: elisaTheme.smallDelegateToolButtonSize implicitWidth: elisaTheme.smallDelegateToolButtonSize opacity: 0 scale: LayoutMirroring.enabled ? -1 : 1 // We can mirror the symmetrical pause icon visible: opacity > 0.1 action: !(isPlaying === MediaPlayList.IsPlaying) ? playNow : pauseNow Layout.alignment: Qt.AlignVCenter | Qt.AlignRight } Item { implicitHeight: elisaTheme.smallDelegateToolButtonSize implicitWidth: elisaTheme.smallDelegateToolButtonSize Layout.maximumWidth: elisaTheme.smallDelegateToolButtonSize Layout.maximumHeight: elisaTheme.smallDelegateToolButtonSize Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Controls1.ToolButton { id: removeButton objectName: 'removeButton' anchors.fill: parent opacity: 0 visible: opacity > 0.1 action: removeFromPlayList } Image { id: playIcon anchors.fill: parent opacity: 0 - source: Qt.resolvedUrl(elisaTheme.playIndicatorIcon) + source: (isPlaying === MediaPlayList.IsPlaying ? + Qt.resolvedUrl(elisaTheme.playingIndicatorIcon) : Qt.resolvedUrl(elisaTheme.pausedIndicatorIcon)) width: parent.height * 1. height: parent.height * 1. sourceSize.width: parent.height * 1. sourceSize.height: parent.height * 1. fillMode: Image.PreserveAspectFit mirror: LayoutMirroring.enabled visible: opacity > 0.0 } } RatingStar { id: ratingWidget starRating: dataHelper.rating starSize: elisaTheme.ratingStarSize } TextMetrics { id: durationTextMetrics text: i18nc("This is used to preserve a fixed width for the duration text.", "00:00") } LabelWithToolTip { id: durationLabel text: dataHelper.duration color: myPalette.text Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.preferredWidth: durationTextMetrics.width + 1 horizontalAlignment: Text.AlignRight } } } } } states: [ State { name: 'notSelected' when: !containsMouse && (!playListEntry.activeFocus || !isSelected) PropertyChanges { target: removeButton opacity: 0 } PropertyChanges { target: infoButton opacity: 0 } PropertyChanges { target: playPauseButton opacity: 0 } PropertyChanges { target: playIcon - opacity: (isPlaying === MediaPlayList.IsPlaying ? 1.0 : 0.0) + opacity: (isPlaying === MediaPlayList.IsPlaying || isPlaying === MediaPlayList.IsPaused ? 1.0 : 0.0) } PropertyChanges { target: entryBackground color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) } }, State { name: 'hoveredOrSelected' when: containsMouse || (playListEntry.activeFocus && isSelected) PropertyChanges { target: removeButton opacity: 1 } PropertyChanges { target: playPauseButton opacity: 1 } PropertyChanges { target: infoButton opacity: 1 } PropertyChanges { target: playIcon opacity: 0 } PropertyChanges { target: entryBackground color: myPalette.mid } } ] transitions: Transition { ParallelAnimation { NumberAnimation { properties: "opacity" easing.type: Easing.InOutQuad duration: 250 } ColorAnimation { properties: "color" duration: 250 } } } } diff --git a/src/qml/Theme.qml b/src/qml/Theme.qml index c1a3fcab..a1b5982d 100644 --- a/src/qml/Theme.qml +++ b/src/qml/Theme.qml @@ -1,97 +1,98 @@ /* * Copyright 2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 Item { function dp(pixel) { // 96 - common, "base" DPI value return Math.round(pixel * logicalDpi / 96); } property string defaultAlbumImage: 'image://icon/media-optical-audio' property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string artistIcon: 'image://icon/view-media-artist' property string albumIcon: 'image://icon/view-media-album-cover' property string playlistIcon: 'image://icon/view-media-playlist' property string tracksIcon: 'image://icon/view-media-track' property string genresIcon: 'image://icon/view-media-genre' property string clearIcon: 'image://icon/edit-clear' property string skipBackwardIcon: 'image://icon/media-skip-backward' property string pauseIcon: 'image://icon/media-playback-pause' property string playIcon: 'image://icon/media-playback-start' property string skipForwardIcon: 'image://icon/media-skip-forward' + property string pausedIndicatorIcon: 'image://icon/media-playback-paused' + property string playingIndicatorIcon: 'image://icon/media-playback-playing' property string playerVolumeMutedIcon: 'image://icon/player-volume-muted' property string playerVolumeIcon: 'image://icon/player-volume' property string ratingIcon: 'image://icon/rating' property string ratingUnratedIcon: 'image://icon/rating-unrated' property string errorIcon: 'image://icon/error' - property string playIndicatorIcon: 'image://icon/audio-volume-high' property string repeatIcon: 'image://icon/media-repeat-all' property string shuffleIcon: 'image://icon/media-playlist-shuffle' property string noRepeatIcon: 'image://icon/media-repeat-none' property string noShuffleIcon: 'image://icon/media-playlist-normal' property string folderIcon: 'image://icon/document-open-folder' property int layoutHorizontalMargin: dp(8) property int layoutVerticalMargin: dp(6) property int delegateHeight: dp(28) property int delegateWithHeaderHeight: dp(68) property int trackDelegateHeight: dp(45) property int coverImageSize: dp(180) property int smallImageSize: dp(32) property int trackMetadataWidth: dp(300) property int tooltipRadius: dp(3) property int shadowOffset: dp(2) property int delegateToolButtonSize: dp(34) property int smallDelegateToolButtonSize: dp(20) property int ratingStarSize: dp(15) property int mediaPlayerControlHeight: dp(42) property int mediaPlayerHorizontalMargin: dp(10) property real mediaPlayerControlOpacity: 0.6 property int smallControlButtonSize: dp(22) property int volumeSliderWidth: dp(100) property int dragDropPlaceholderHeight: dp(28) property int navigationBarHeight: dp(100) property int navigationBarFilterHeight: dp(44) property int gridDelegateHeight: dp(100) + layoutVerticalMargin + fontSize.height * 2 property int gridDelegateWidth: dp(100) property int viewSelectorDelegateHeight: dp(24) property int filterClearButtonMargin: layoutVerticalMargin property alias defaultFontPointSize: fontSize.font.pointSize Label { id: fontSize } } diff --git a/src/trackdatahelper.cpp b/src/trackdatahelper.cpp index 7f15f0a3..0d329b0d 100644 --- a/src/trackdatahelper.cpp +++ b/src/trackdatahelper.cpp @@ -1,155 +1,153 @@ /* * Copyright 2018 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "trackdatahelper.h" -#include - TrackDataHelper::TrackDataHelper(QObject *parent) : QObject(parent) { } TrackDataHelper::~TrackDataHelper() = default; const MusicAudioTrack &TrackDataHelper::trackData() const { return *this; } void TrackDataHelper::setTrackData(const MusicAudioTrack &track) { MusicAudioTrack::operator=(track); Q_EMIT trackDataChanged(); } QString TrackDataHelper::title() const { if (MusicAudioTrack::title().isEmpty()) { return fileName(); } else { return MusicAudioTrack::title(); } } QString TrackDataHelper::duration() const { QString result; const QTime &trackDuration = MusicAudioTrack::duration(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(QStringLiteral("h:mm:ss")); } return result; } QString TrackDataHelper::resourceURI() const { return MusicAudioTrack::resourceURI().toDisplayString(QUrl::PreferLocalFile); } QString TrackDataHelper::fileName() const { return MusicAudioTrack::resourceURI().fileName(); } bool TrackDataHelper::hasValidTrackNumber() const { return trackNumber() > -1; } bool TrackDataHelper::hasValidDiscNumber() const { return discNumber() > -1; } bool TrackDataHelper::hasValidChannels() const { return channels() > -1; } bool TrackDataHelper::hasValidBitRate() const { return bitRate() > -1; } bool TrackDataHelper::hasValidSampleRate() const { return sampleRate() > -1; } bool TrackDataHelper::hasValidYear() const { return MusicAudioTrack::year() != 0; } bool TrackDataHelper::hasValidRating() const { return rating() > -1; } bool TrackDataHelper::hasValidTitle() const { return !title().isEmpty(); } bool TrackDataHelper::hasValidArtist() const { return !artist().isEmpty(); } bool TrackDataHelper::hasValidAlbumArtist() const { return !albumArtist().isEmpty(); } bool TrackDataHelper::hasValidAlbumName() const { return !albumName().isEmpty(); } bool TrackDataHelper::hasValidGenre() const { return !genre().isEmpty(); } bool TrackDataHelper::hasValidComposer() const { return !composer().isEmpty(); } bool TrackDataHelper::hasValidLyricist() const { return !lyricist().isEmpty(); } bool TrackDataHelper::hasValidComment() const { return !comment().isEmpty(); } bool TrackDataHelper::hasValidAlbumCover() const { return !albumCover().isEmpty(); } #include "moc_trackdatahelper.cpp" diff --git a/src/windows/PlatformIntegration.qml b/src/windows/PlatformIntegration.qml index e982a1cf..ab8fab62 100644 --- a/src/windows/PlatformIntegration.qml +++ b/src/windows/PlatformIntegration.qml @@ -1,70 +1,71 @@ /* * Copyright 2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import org.kde.elisa 1.0 import QtWinExtras 1.0 Item { id: rootItem property var playListModel property var playListControler property var audioPlayerManager property var player property var headerBarManager property var manageMediaPlayerControl signal raisePlayer() Theme { id: elisaTheme } TaskbarButton { progress.minimum: 0 progress.maximum: player.duration progress.value: player.position progress.visible: manageMediaPlayerControl.musicPlaying - overlay.iconSource: (manageMediaPlayerControl.musicPlaying ? Qt.resolvedUrl(elisaTheme.playIcon) : Qt.resolvedUrl(elisaTheme.pauseIcon)) + overlay.iconSource: (manageMediaPlayerControl.musicPlaying ? + Qt.resolvedUrl(elisaTheme.playingIndicatorIcon) : Qt.resolvedUrl(elisaTheme.pausedIndicatorIcon)) } ThumbnailToolBar { iconicThumbnailSource: (headerBarManager.image.toString() !== '' ? headerBarManager.image : Qt.resolvedUrl(elisaTheme.albumCover)) ThumbnailToolButton { iconSource: Qt.resolvedUrl(LayoutMirroring.enabled ? elisaTheme.skipForwardIcon : elisaTheme.skipBackwardIcon) onClicked: playListControler.skipPreviousTrack() enabled: manageMediaPlayerControl.skipBackwardControlEnabled } ThumbnailToolButton { iconSource: (manageMediaPlayerControl.musicPlaying ? Qt.resolvedUrl(elisaTheme.pauseIcon) : Qt.resolvedUrl(elisaTheme.playIcon)) onClicked: audioPlayerManager.playPause() enabled: manageMediaPlayerControl.playControlEnabled } ThumbnailToolButton { iconSource: Qt.resolvedUrl(LayoutMirroring.enabled ? elisaTheme.skipBackwardIcon : elisaTheme.skipForwardIcon) onClicked: playListControler.skipNextTrack() enabled: manageMediaPlayerControl.skipForwardControlEnabled } } } diff --git a/src/windows/WindowsTheme.qml b/src/windows/WindowsTheme.qml index 1c322532..71b1b93c 100644 --- a/src/windows/WindowsTheme.qml +++ b/src/windows/WindowsTheme.qml @@ -1,96 +1,98 @@ /* * Copyright 2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 1.4 Item { function dp(pixel) { // 96 - common, "base" DPI value return Math.round(pixel * logicalDpi / 96); } property string defaultAlbumImage: 'image://icon/media-optical-audio' property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string artistIcon: 'image://icon/view-media-artist' property string albumIcon: 'image://icon/view-media-album-cover' property string playlistIcon: 'image://icon/view-media-playlist' property string tracksIcon: 'image://icon/view-media-track' property string genresIcon: 'image://icon/view-media-genre' property string clearIcon: 'image://icon/edit-clear' property string skipBackwardIcon: 'image://icon/media-skip-backward' property string pauseIcon: 'image://icon/media-playback-pause' property string playIcon: 'image://icon/media-playback-start' property string skipForwardIcon: 'image://icon/media-skip-forward' + property string pausedIndicatorIcon: 'image://icon/media-playback-paused' + property string playingIndicatorIcon: 'image://icon/media-playback-playing' property string playerVolumeMutedIcon: 'image://icon/player-volume-muted' property string playerVolumeIcon: 'image://icon/player-volume' property string ratingIcon: 'image://icon/rating' property string ratingUnratedIcon: 'image://icon/rating-unrated' property string errorIcon: 'image://icon/error' property string repeatIcon: 'image://icon/media-repeat-all' property string shuffleIcon: 'image://icon/media-playlist-shuffle' property string noRepeatIcon: 'image://icon/media-repeat-none' property string noShuffleIcon: 'image://icon/media-playlist-normal' property string folderIcon: 'image://icon/document-open-folder' property int layoutHorizontalMargin: dp(8) property int layoutVerticalMargin: dp(6) property int delegateHeight: dp(28) property int delegateWithHeaderHeight: dp(68) property int trackDelegateHeight: dp(45) property int coverImageSize: dp(180) property int smallImageSize: dp(32) property int trackMetadataWidth: dp(300) property int tooltipRadius: dp(3) property int shadowOffset: dp(2) property int delegateToolButtonSize: dp(34) property int smallDelegateToolButtonSize: dp(20) property int ratingStarSize: dp(15) property int mediaPlayerControlHeight: dp(42) property int mediaPlayerHorizontalMargin: dp(10) property real mediaPlayerControlOpacity: 0.8 property int smallControlButtonSize: dp(22) property int volumeSliderWidth: dp(100) property int dragDropPlaceholderHeight: dp(28) property int navigationBarHeight: dp(100) property int navigationBarFilterHeight: dp(44) property int gridDelegateHeight: dp(100) + layoutVerticalMargin + fontSize.height * 2 property int gridDelegateWidth: dp(100) property int viewSelectorDelegateHeight: dp(24) property int filterClearButtonMargin: 1 property alias defaultFontPointSize: fontSize.font.pointSize Label { id: fontSize } }