diff --git a/src/qml/MediaPlayListView.qml b/src/qml/MediaPlayListView.qml index 61d223cf..419e777e 100644 --- a/src/qml/MediaPlayListView.qml +++ b/src/qml/MediaPlayListView.qml @@ -1,319 +1,319 @@ /* * Copyright 2016-2017 Matthieu Gallien * Copyright 2019 Nate Graham * * 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.5 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import Qt.labs.platform 1.0 as PlatformDialog import org.kde.kirigami 2.5 as Kirigami import org.kde.elisa 1.0 FocusScope { property StackView parentStackView property int placeholderHeight: elisaTheme.dragDropPlaceholderHeight signal startPlayback() signal pausePlayback() function showPlayListNotification(message, type, action) { if (!message) { return; } if (type) { playListNotification.type = type; } else { playListNotification.type = Kirigami.MessageType.Information; } if (action) { playListNotification.actions = action; } else { playListNotification.actions = []; } playListNotification.text = message ? message : ""; playListNotification.visible = true; } function hideNotification() { playListNotification.visible = false; } Kirigami.Action { id: undoAction text: i18nc("Undo", "Undo") icon.name: "dialog-cancel" onTriggered: elisa.mediaPlayListProxyModel.undoClearPlayList() } Kirigami.Action { id: retryLoadAction text: i18nc("Retry", "Retry") icon.name: "edit-redo" onTriggered: loadPlaylistButton.clicked() } Kirigami.Action { id: retrySaveAction text: i18nc("Retry", "Retry") icon.name: "edit-redo" onTriggered: savePlaylistButton.clicked() } Connections { target: elisa.mediaPlayListProxyModel onPlayListLoadFailed: { showPlayListNotification(i18nc("Message when playlist load failed", "Loading failed"), Kirigami.MessageType.Error, retryLoadAction) } } Connections { target: elisa.mediaPlayListProxyModel onDisplayUndoNotification: { showPlayListNotification(i18nc("Playlist cleared", "Playlist cleared"), Kirigami.MessageType.Information, undoAction) } } Connections { target: elisa.mediaPlayListProxyModel onHideUndoNotification: hideNotification() } id: topItem Accessible.role: Accessible.Pane Accessible.name: viewTitle.text PlatformDialog.FileDialog { id: fileDialog defaultSuffix: 'm3u' folder: PlatformDialog.StandardPaths.writableLocation(PlatformDialog.StandardPaths.MusicLocation) nameFilters: [i18nc("file type (mime type) for m3u playlist", "Playlist (*.m3u)")] onAccepted: { if (fileMode === PlatformDialog.FileDialog.SaveFile) { if (!elisa.mediaPlayListProxyModel.savePlayList(fileDialog.file)) { showPlayListNotification(i18nc("Message when saving a playlist failed", "Saving failed"), Kirigami.MessageType.Error, retrySaveAction) } } else { elisa.mediaPlayListProxyModel.loadPlayList(fileDialog.file) } } } ColumnLayout { anchors.fill: parent spacing: 0 // Header with title and toolbar buttons HeaderFooterToolbar { type: "header" contentItems: [ // Header title LabelWithToolTip { id: viewTitle Layout.fillWidth: true text: i18nc("Title of the view of the playlist", "Playlist") level: 1 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter }, // Toolbar buttons FlatButtonWithToolTip { text: i18nc("Show currently played track inside playlist", "Show Current Track") - icon.name: 'media-show-active-track-amarok' + icon.name: 'media-track-show-active' enabled: elisa.mediaPlayListProxyModel ? elisa.mediaPlayListProxyModel.tracksCount > 0 : false onClicked: { playListView.positionViewAtIndex(elisa.mediaPlayListProxyModel.currentTrackRow, ListView.Contain) playListView.currentIndex = elisa.mediaPlayListProxyModel.currentTrackRow playListView.currentItem.forceActiveFocus() } }, FlatButtonWithToolTip { id: savePlaylistButton text: i18nc("Save a playlist file", "Save Playlist...") icon.name: 'document-save' enabled: elisa.mediaPlayListProxyModel ? elisa.mediaPlayListProxyModel.tracksCount > 0 : false onClicked: { fileDialog.fileMode = PlatformDialog.FileDialog.SaveFile fileDialog.file = '' fileDialog.open() } }, FlatButtonWithToolTip { id: loadPlaylistButton text: i18nc("Load a playlist file", "Load Playlist...") icon.name: 'document-open' onClicked: { fileDialog.fileMode = PlatformDialog.FileDialog.OpenFile fileDialog.file = '' fileDialog.open() } }, FlatButtonWithToolTip { text: i18nc("Remove all tracks from play list", "Clear Playlist") icon.name: 'edit-clear-all' enabled: elisa.mediaPlayListProxyModel ? elisa.mediaPlayListProxyModel.tracksCount > 0 : false onClicked: elisa.mediaPlayListProxyModel.clearPlayList() } ] } ColumnLayout { id: emptyPlaylistText spacing: 0 visible: true Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Layout.fillHeight: true Layout.fillWidth: true Item { id: emptyVisible visible: elisa.mediaPlayListProxyModel ? elisa.mediaPlayListProxyModel.tracksCount === 0 : true Layout.fillHeight: true } Image { id: emptyImage visible: emptyVisible.visible Layout.alignment: Qt.AlignHCenter width: elisaTheme.gridDelegateSize height: elisaTheme.gridDelegateSize source: elisaTheme.playlistIcon opacity: 0.25 sourceSize { width: elisaTheme.gridDelegateSize height: elisaTheme.gridDelegateSize } } LabelWithToolTip { id: emptyLabel0 visible: emptyVisible.visible Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter Layout.rightMargin: Kirigami.Units.largeSpacing Layout.leftMargin: Kirigami.Units.largeSpacing level: 1 wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter text: i18nc("Your playlist is empty", "Your playlist is empty") } Label { id: emptyLabel1 visible: emptyVisible.visible Layout.topMargin: Kirigami.Units.largeSpacing Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter Layout.rightMargin: Kirigami.Units.largeSpacing Layout.leftMargin: Kirigami.Units.largeSpacing wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter text: i18nc("Text shown when play list is empty", "Add some songs to get started. You can browse your music using the views on the left.") } Item { visible: emptyVisible.visible Layout.fillHeight: true } PlayListBasicView { id: playListView visible: !emptyVisible.visible Layout.fillWidth: true Layout.fillHeight: true title: viewTitle.text playListModel: elisa.mediaPlayListProxyModel focus: true onStartPlayback: topItem.startPlayback() onPausePlayback: topItem.pausePlayback() onDisplayError: showPlayListNotification(errorText, Kirigami.MessageType.Error) } Kirigami.InlineMessage { id: playListNotification Timer { id: autoHideNotificationTimer interval: 7000 onTriggered: playListNotification.visible = false } type: Kirigami.MessageType.Information showCloseButton: true Layout.topMargin: Kirigami.Units.largeSpacing Layout.fillWidth: true Layout.rightMargin: Kirigami.Units.largeSpacing Layout.leftMargin: Kirigami.Units.largeSpacing onVisibleChanged: { if (visible) { autoHideNotificationTimer.start() } else { autoHideNotificationTimer.stop() } } } } // Footer with number of tracks label HeaderFooterToolbar { type: "footer" contentItems: [ LabelWithToolTip { id: trackCountLabel Layout.fillWidth: true text: i18np("1 track", "%1 tracks", (elisa.mediaPlayListProxyModel ? elisa.mediaPlayListProxyModel.tracksCount : 0)) elide: Text.ElideLeft } ] } } } diff --git a/src/qml/NativeApplicationMenu.qml b/src/qml/NativeApplicationMenu.qml index 4b04eff3..3ff50e85 100644 --- a/src/qml/NativeApplicationMenu.qml +++ b/src/qml/NativeApplicationMenu.qml @@ -1,114 +1,114 @@ /* * 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 //explore menu from Qt 5.10 once we can require it, but it is item-based import Qt.labs.platform 1.0 import org.kde.elisa 1.0 Menu { id: applicationMenu title: i18nc("open application menu", "Application Menu") property var helpAction: elisa.action("help_contents") property var quitApplication: elisa.action("file_quit") property var reportBugAction: elisa.action("help_report_bug") property var aboutAppAction: elisa.action("help_about_app") property var configureShortcutsAction: elisa.action("options_configure_keybinding") property var configureAction: elisa.action("options_configure") property var togglePlaylistAction: elisa.action("toggle_playlist") MenuItem { text: i18nc("Refresh Music Collection application menu entry", "Refresh Music Collection") - iconName: "collection-rescan-amarok" + iconName: "view-refresh" onTriggered: elisa.musicManager.resetMusicData() } MenuSeparator { } MenuItem { text: configureAction.text shortcut: configureAction.shortcut iconName: elisa.iconName(configureAction.icon) onTriggered: configureAction.trigger() visible: configureAction.text !== "" } MenuItem { text: configureShortcutsAction.text shortcut: configureShortcutsAction.shortcut iconName: elisa.iconName(configureShortcutsAction.icon) onTriggered: configureShortcutsAction.trigger() visible: configureShortcutsAction.text !== "" } MenuItem { shortcut: togglePlaylistAction.shortcut text: i18n("Show Playlist") iconName: "view-media-playlist" checkable: true checked: contentView.showPlaylist onTriggered: contentView.showPlaylist = !contentView.showPlaylist enabled: contentView.currentViewIndex != 0 } MenuSeparator { visible: reportBugAction.text !== "" } MenuItem { text: reportBugAction.text shortcut: reportBugAction.shortcut iconName: elisa.iconName(reportBugAction.icon) onTriggered: reportBugAction.trigger() visible: reportBugAction.text !== "" } MenuSeparator { visible: helpAction.text !== "" } MenuItem { text: helpAction.text shortcut: helpAction.shortcut iconName: elisa.iconName(helpAction.icon) onTriggered: helpAction.trigger() visible: helpAction.text !== "" } MenuItem { text: aboutAppAction.text shortcut: aboutAppAction.shortcut iconName: elisa.iconName(aboutAppAction.icon) onTriggered: aboutAppAction.trigger() visible: aboutAppAction.text !== "" } MenuSeparator { visible: quitApplication.text !== "" } MenuItem { text: quitApplication.text shortcut: quitApplication.shortcut iconName: elisa.iconName(quitApplication.icon) onTriggered: quitApplication.trigger() visible: quitApplication.text !== "" } } diff --git a/src/qml/NavigationActionBar.qml b/src/qml/NavigationActionBar.qml index 69d61301..597feeb7 100644 --- a/src/qml/NavigationActionBar.qml +++ b/src/qml/NavigationActionBar.qml @@ -1,241 +1,241 @@ /* * 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.3 import org.kde.kirigami 2.8 as Kirigami ColumnLayout { id: navigationBar spacing: 0 property string mainTitle property string secondaryTitle property url image property bool allowArtistNavigation: false property bool showEnqueueButton: true property bool showCreateRadioButton property string labelText property bool showRating: true property alias filterText: filterTextInput.text property alias filterRating: ratingFilter.starRating property bool enableGoBack: true property bool expandedFilterView property bool enableSorting: true property bool sortOrder signal enqueue(); signal replaceAndPlay(); signal createRadio(); signal goBack(); signal showArtist(); signal sort(var order); HeaderFooterToolbar { type: filterRow.visible? "other" : "header" contentItems: [ FlatButtonWithToolTip { id: goPreviousButton objectName: 'goPreviousButton' visible: enableGoBack text: i18nc("navigate back in the views stack", "Back") icon.name: (Qt.application.layoutDirection == Qt.RightToLeft) ? "go-next" : "go-previous" onClicked: goBack() }, Item { id: spacer Layout.preferredWidth: Kirigami.Units.largeSpacing visible: goPreviousButton.visible }, Image { id: mainIcon source: image Layout.preferredHeight: authorAndAlbumLayout.height Layout.preferredWidth: height sourceSize.height: height sourceSize.width: width fillMode: Image.PreserveAspectFit asynchronous: true Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft }, Item { Layout.preferredWidth: Kirigami.Units.largeSpacing visible: mainIcon.visible }, ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true id: authorAndAlbumLayout LabelWithToolTip { id: albumLabel Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.fillWidth: true text: mainTitle level: authorLabel.visible ? 4 : 1 font.weight: authorLabel.visible ? Font.Bold : Font.Normal elide: Text.ElideRight } LabelWithToolTip { id: authorLabel Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.fillWidth: true text: secondaryTitle elide: Text.ElideRight visible: secondaryTitle !== "" } }, FlatButtonWithToolTip { objectName: 'createRadioButton' visible: showCreateRadioButton text: i18nc("Create a new radio", "Create a radio") - icon.name: "media-track-add-amarok" + icon.name: "list-add" onClicked: createRadio() }, FlatButtonWithToolTip { objectName: 'enqueueButton' visible: !showCreateRadioButton text: i18nc("Add current list to playlist", "Enqueue") icon.name: "list-add" onClicked: enqueue() }, FlatButtonWithToolTip { objectName: 'replaceAndPlayButton' visible: !showCreateRadioButton text: i18n("Play now, replacing contents of Playlist") icon.name: "media-playback-start" onClicked: replaceAndPlay() }, FlatButtonWithToolTip { objectName: 'showArtistButton' visible: allowArtistNavigation && !showCreateRadioButton text: i18nc("Button to navigate to the artist of the album", "Display Artist") icon.name: "view-media-artist" onClicked: showArtist() }, FlatButtonWithToolTip { objectName: 'sortAscendingButton' visible: enableSorting && !showCreateRadioButton text: i18nc("Toggle between ascending and descending order", "Toggle sort order") icon.name: sortOrder ? "view-sort-ascending" : "view-sort-descending" onClicked: sortOrder ? sort(Qt.DescendingOrder) : sort(Qt.AscendingOrder) }, FlatButtonWithToolTip { objectName: 'showFilterButton' visible: !showCreateRadioButton text: !navigationBar.expandedFilterView ? i18nc("Show filters in the navigation bar", "Show Search Options") : i18nc("Hide filters in the navigation bar", "Hide Search Options") icon.name: 'search' checkable: true checked: expandedFilterView onClicked: persistentSettings.expandedFilterView = !persistentSettings.expandedFilterView } ] } HeaderFooterToolbar { type: "header" id: filterRow visible: opacity > 0.0 opacity: 0 contentItems: [ Kirigami.SearchField { id: filterTextInput objectName: 'filterTextInput' Layout.fillWidth: true focusSequence: "" selectByMouse: true Accessible.role: Accessible.EditableText placeholderText: i18n("Search for album name, artist, etc.") Keys.onEscapePressed: persistentSettings.expandedFilterView = false; }, Item { width: Kirigami.Units.largeSpacing }, LabelWithToolTip { text: i18n("Filter by rating: ") visible: showRating }, RatingStar { id: ratingFilter objectName: 'ratingFilter' visible: showRating hoverWidgetOpacity: 1 readOnly: false } ] } states: [ State { name: 'collapsed' when: !expandedFilterView PropertyChanges { target: filterRow opacity: 0.0 } StateChangeScript { // Focus main content view since that's probably what the user // wants to interact with next script: contentDirectoryView.forceActiveFocus(); } }, State { name: 'expanded' when: expandedFilterView PropertyChanges { target: filterRow opacity: 1.0 } StateChangeScript { script: filterTextInput.forceActiveFocus() } } ] transitions: Transition { from: "expanded,collapsed" PropertyAnimation { properties: "opacity" easing.type: Easing.Linear duration: Kirigami.Units.longDuration } } }