diff --git a/src/qml/BaseTheme.qml b/src/qml/BaseTheme.qml index 339a1cd1..94ccad3b 100644 --- a/src/qml/BaseTheme.qml +++ b/src/qml/BaseTheme.qml @@ -1,66 +1,64 @@ /* * Copyright 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 Item { property string defaultAlbumImage: 'image://icon/media-optical-audio' property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string nowPlayingIcon: 'image://icon/view-media-lyrics' property string artistIcon: 'image://icon/view-media-artist' property string albumIcon: 'image://icon/view-media-album-cover' property string albumCoverIcon: 'image://icon/media-optical-audio' 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 recentlyPlayedTracksIcon: 'image://icon/media-playlist-play' property string frequentlyPlayedTracksIcon: 'image://icon/view-media-playcount' property string pausedIndicatorIcon: 'image://icon/media-playback-paused' property string playingIndicatorIcon: 'image://icon/media-playback-playing' property string ratingIcon: 'image://icon/rating' property string ratingUnratedIcon: 'image://icon/rating-unrated' property string errorIcon: 'image://icon/error' property string folderIcon: 'image://icon/document-open-folder' property int playListAlbumArtSize: 60 property int coverImageSize: 180 property int contextCoverImageSize: 100 property int smallImageSize: 32 property int tooltipRadius: 3 property int shadowOffset: 2 property int delegateToolButtonSize: 34 property int mediaPlayerControlHeight: 42 property real mediaPlayerControlOpacity: 0.6 property int volumeSliderWidth: 100 property int dragDropPlaceholderHeight: 28 property int gridDelegateSize: 170 - property int viewSelectorDelegateHeight: 24 - property int headerToolbarHeight: 48 property int footerToolbarHeight: 30 property int viewSelectorSmallSizeThreshold: 800 } diff --git a/src/qml/MediaPlayListView.qml b/src/qml/MediaPlayListView.qml index 00f3c235..004e0574 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.mediaPlayList.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.mediaPlayList onPlayListLoadFailed: { showPlayListNotification(i18nc("Message when playlist load failed", "Loading failed"), Kirigami.MessageType.Error, retryLoadAction) } } Connections { target: elisa.mediaPlayList onDisplayUndoNotification: { showPlayListNotification(i18nc("Playlist cleared", "Playlist cleared"), Kirigami.MessageType.Information, undoAction) } } Connections { target: elisa.mediaPlayList 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.mediaPlayList.savePlaylist(fileDialog.file)) { showPlayListNotification(i18nc("Message when saving a playlist failed", "Saving failed"), Kirigami.MessageType.Error, retrySaveAction) } } else { elisa.mediaPlayList.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' enabled: elisa.mediaPlayList ? elisa.mediaPlayList.tracksCount > 0 : false onClicked: { playListView.positionViewAtIndex(elisa.mediaPlayList.currentTrackRow, ListView.Contain) playListView.currentIndex = elisa.mediaPlayList.currentTrackRow playListView.currentItem.forceActiveFocus() } }, FlatButtonWithToolTip { id: savePlaylistButton text: i18nc("Save a playlist file", "Save Playlist...") icon.name: 'document-save' enabled: elisa.mediaPlayList ? elisa.mediaPlayList.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.mediaPlayList ? elisa.mediaPlayList.tracksCount > 0 : false onClicked: elisa.mediaPlayList.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.mediaPlayList ? elisa.mediaPlayList.tracksCount === 0 : true Layout.preferredHeight: (emptyPlaylistText.height-emptyImage.height-emptyLabel0.height-emptyLabel1.height)/2 } Image { id: emptyImage visible: emptyVisible.visible Layout.alignment: Qt.AlignHCenter - width: elisaTheme.gridDelegateWidth * 5 - height: elisaTheme.gridDelegateWidth * 5 + width: elisaTheme.gridDelegateSize + height: elisaTheme.gridDelegateSize source: elisaTheme.playlistIcon opacity: 0.25 sourceSize { - width: elisaTheme.viewSelectorDelegateHeight * 5 - height: elisaTheme.viewSelectorDelegateHeight * 5 + 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.mediaPlayList 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.mediaPlayList ? elisa.mediaPlayList.tracksCount : 0)) elide: Text.ElideLeft } ] } } } diff --git a/src/qml/ViewSelector.qml b/src/qml/ViewSelector.qml index 584a1377..fffe91aa 100644 --- a/src/qml/ViewSelector.qml +++ b/src/qml/ViewSelector.qml @@ -1,143 +1,144 @@ /* * 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 QtQml.Models 2.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.5 as Kirigami import org.kde.elisa 1.0 FocusScope { id: rootFocusScope readonly property alias currentIndex: viewModeView.currentIndex property double textOpacity property alias model: pageDelegateModel.model signal switchView(var viewType) function setCurrentIndex(index) { viewModeView.ignoreCurrentItemChanges = true viewModeView.currentIndex = index viewModeView.ignoreCurrentItemChanges = false } implicitWidth: 225 ScrollView { focus: true anchors.fill: parent z: 2 clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ListView { id: viewModeView Accessible.role: Accessible.List focus: true activeFocusOnTab: true keyNavigationEnabled: true property bool ignoreCurrentItemChanges: false z: 2 anchors.topMargin: Kirigami.Units.largeSpacing * 2 model: DelegateModel { id: pageDelegateModel delegate: ViewSelectorDelegate { id: entry - height: Math.round(elisaTheme.viewSelectorDelegateHeight * 1.4) + height: Kirigami.Units.iconSizes.smallMedium + 3 * Kirigami.Units.smallSpacing width: viewModeView.width focus: true isSelected: viewModeView.currentIndex === index onClicked: { viewModeView.currentIndex = index entry.forceActiveFocus() } } } footer: MouseArea { width: viewModeView.width height: viewModeView.height - y acceptedButtons: Qt.LeftButton onClicked: { rootFocusScope.focus = true } } onCurrentItemChanged: if (!ignoreCurrentItemChanges) switchView(currentItem.viewType) } } Connections { target: elisa onInitializationDone: { viewModeView.currentIndex = 3 } } Behavior on implicitWidth { NumberAnimation { duration: 150 } } Behavior on width { NumberAnimation { duration: 150 } } states: [ State { name: 'iconsAndText' when: mainWindow.width >= elisaTheme.viewSelectorSmallSizeThreshold PropertyChanges { target: rootFocusScope textOpacity: 1 implicitWidth: 225 } }, State { name: 'iconsOnly' when: mainWindow.width < elisaTheme.viewSelectorSmallSizeThreshold PropertyChanges { target: rootFocusScope textOpacity: 0 - implicitWidth: elisaTheme.viewSelectorDelegateHeight + 2 * Kirigami.Units.largeSpacing + implicitWidth: Kirigami.Units.iconSizes.smallMedium + 3 * Kirigami.Units.smallSpacing + } } ] } diff --git a/src/qml/ViewSelectorDelegate.qml b/src/qml/ViewSelectorDelegate.qml index 6e1b026a..eaae4372 100644 --- a/src/qml/ViewSelectorDelegate.qml +++ b/src/qml/ViewSelectorDelegate.qml @@ -1,239 +1,239 @@ /* * Copyright 2016-2019 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 QtGraphicalEffects 1.0 import org.kde.kirigami 2.5 as Kirigami FocusScope { id: rootItem property var viewType: model.type property bool isSelected signal clicked() Rectangle { id: backgroundHighlight anchors.fill: parent z: 1 color: "transparent" } Accessible.role: Accessible.ListItem Accessible.description: model.display Accessible.name: model.display MouseArea { id: hoverArea anchors.fill: parent z: 2 hoverEnabled: true acceptedButtons: Qt.LeftButton onClicked: { rootItem.clicked() } Loader { id: hoverLoader anchors.fill: parent active: false sourceComponent: ToolTip { delay: Qt.styleHints.mousePressAndHoldInterval text: model.display visible: hoverArea && hoverArea.containsMouse && !nameLabel.visible contentItem: Label { text: model.display color: myPalette.highlightedText } enter: Transition { NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; from: 0.0; to: 1.0; duration: 300; } } exit: Transition { NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; from: 1.0; to: 0.0; duration: 300; } } background: Rectangle { color: myPalette.shadow radius: elisaTheme.tooltipRadius layer.enabled: true layer.effect: DropShadow { horizontalOffset: elisaTheme.shadowOffset verticalOffset: elisaTheme.shadowOffset radius: 8 samples: 17 color: myPalette.shadow } } } } Image { id: viewIcon z: 2 anchors { verticalCenter: parent.verticalCenter leftMargin: Kirigami.Units.largeSpacing left: parent.left } - height: elisaTheme.viewSelectorDelegateHeight - width: elisaTheme.viewSelectorDelegateHeight + height: Kirigami.Units.iconSizes.smallMedium + width: Kirigami.Units.iconSizes.smallMedium sourceSize { - width: elisaTheme.viewSelectorDelegateHeight - height: elisaTheme.viewSelectorDelegateHeight + width: Kirigami.Units.iconSizes.smallMedium + height: Kirigami.Units.iconSizes.smallMedium } source: model.image layer.enabled: true layer.effect: ColorOverlay { color: nameLabel.color } } LabelWithToolTip { id: nameLabel z: 2 anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: Kirigami.Units.largeSpacing anchors.left: viewIcon.right anchors.right: parent.right anchors.rightMargin: Kirigami.Units.largeSpacing verticalAlignment: "AlignVCenter" text: model.display elide: Text.ElideRight opacity: textOpacity visible: opacity > 0 color: (viewModeView.currentIndex === index || hoverArea.containsMouse ? myPalette.highlight : myPalette.text) } } states: [ State { name: 'notSelected' when: !rootItem.activeFocus && !hoverArea.containsMouse && !rootItem.isSelected PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } PropertyChanges { target: viewIcon opacity: 1 } PropertyChanges { target: nameLabel color: myPalette.buttonText } PropertyChanges { target: backgroundHighlight color: 'transparent' } }, State { name: 'hovered' when: !rootItem.activeFocus && hoverArea.containsMouse PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: hoverLoader opacity: 1.0 } PropertyChanges { target: viewIcon opacity: 0.4 } PropertyChanges { target: nameLabel color: myPalette.buttonText } PropertyChanges { target: backgroundHighlight color: Qt.rgba(myPalette.highlight.r, myPalette.highlight.g, myPalette.highlight.b, 0.2) } }, State { name: 'selected' when: !rootItem.activeFocus && rootItem.isSelected PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } PropertyChanges { target: viewIcon opacity: 1 } PropertyChanges { target: nameLabel color: myPalette.buttonText } PropertyChanges { target: backgroundHighlight color: myPalette.mid } }, State { name: 'focused' when: rootItem.activeFocus PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } PropertyChanges { target: viewIcon opacity: 1. } PropertyChanges { target: nameLabel color: myPalette.highlightedText } PropertyChanges { target: backgroundHighlight color: myPalette.highlight } } ] }