diff --git a/autotests/qmltests/tst_NavigationActionBar.qml b/autotests/qmltests/tst_NavigationActionBar.qml index 245c0100..da2a3fdc 100644 --- a/autotests/qmltests/tst_NavigationActionBar.qml +++ b/autotests/qmltests/tst_NavigationActionBar.qml @@ -1,300 +1,298 @@ /* * Copyright 2018 Alexander Stippich . */ import QtQuick 2.3 import QtTest 1.0 import "../../src/qml" FocusScope { Item { id: persistentSettings property bool expandedFilterView: false } function i18nc(string1,string2) { return string2 } function i18n(string) { return string; } Item { id: elisaTheme property int layoutHorizontalMargin: 8 property int layoutVerticalMargin: 6 - property int ratingStarSize: 15 - property int navigationBarHeight: 100 - property int smallControlButtonSize: 22 + property int ratingStarSize: 16 } SystemPalette { id: myPalette colorGroup: SystemPalette.Active } NavigationActionBar { id: navigationActionBar1 mainTitle: 'testTitle1' secondaryTitle: 'secondaryTitle1' enableGoBack: true allowArtistNavigation: true showRating: true expandedFilterView: persistentSettings.expandedFilterView height: 100 } NavigationActionBar { id: navigationActionBar2 mainTitle: 'testTitle2' secondaryTitle: 'secondaryTitle2' enableGoBack: false allowArtistNavigation: false showRating: false expandedFilterView: persistentSettings.expandedFilterView height: 100 y: 200 } TestCase { name: "TestNavigationActionBar" SignalSpy { id: enqueueSpy1 target: navigationActionBar1 signalName: "enqueue" } SignalSpy { id: enqueueSpy2 target: navigationActionBar2 signalName: "enqueue" } SignalSpy { id: replaceAndPlaySpy1 target: navigationActionBar1 signalName: "replaceAndPlay" } SignalSpy { id: replaceAndPlaySpy2 target: navigationActionBar2 signalName: "replaceAndPlay" } SignalSpy { id: goBackSpy1 target: navigationActionBar1 signalName: "goBack" } SignalSpy { id: goBackSpy2 target: navigationActionBar2 signalName: "goBack" } SignalSpy { id: showArtistSpy1 target: navigationActionBar1 signalName: "showArtist" } SignalSpy { id: showArtistSpy2 target: navigationActionBar2 signalName: "showArtist" } when: windowShown function init() { enqueueSpy1.clear(); enqueueSpy2.clear(); replaceAndPlaySpy1.clear(); replaceAndPlaySpy2.clear(); goBackSpy1.clear(); goBackSpy2.clear(); showArtistSpy1.clear(); showArtistSpy2.clear(); persistentSettings.expandedFilterView = false; } function test_goBack() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var goPreviousButtonItem1 = findChild(navigationActionBar1, "goPreviousButton"); verify(goPreviousButtonItem1 !== null, "valid goPreviousButton") mouseClick(goPreviousButtonItem1); wait(200) compare(goBackSpy1.count, 1); var goPreviousButtonItem2 = findChild(navigationActionBar2, "goPreviousButton"); verify(goPreviousButtonItem2 !== null, "valid goPreviousButton") mouseClick(goPreviousButtonItem2); wait(200) compare(goBackSpy2.count, 0); } function test_enqueue() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var enqueueButtonItem = findChild(navigationActionBar1, "enqueueButton"); verify(enqueueButtonItem !== null, "valid enqueueButton") mouseClick(enqueueButtonItem); compare(enqueueSpy1.count, 1); } function test_filterState() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var showFilterButtonItem1 = findChild(navigationActionBar1, "showFilterButton"); verify(showFilterButtonItem1 !== null, "valid showFilterButton") mouseClick(showFilterButtonItem1); compare(navigationActionBar1.state,'expanded') var showFilterButtonItem2 = findChild(navigationActionBar2, "showFilterButton"); verify(showFilterButtonItem2 !== null, "valid showFilterButton") mouseClick(showFilterButtonItem2); compare(navigationActionBar2.expandedFilterView, false) compare(navigationActionBar2.state, 'collapsed') } function test_replaceAndPlay() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var replaceAndPlayButtonItem = findChild(navigationActionBar1, "replaceAndPlayButton"); verify(replaceAndPlayButtonItem !== null, "valid replaceAndPlayButton") mouseClick(replaceAndPlayButtonItem); compare(replaceAndPlaySpy1.count, 1); } function test_showArtist() { compare(enqueueSpy1.count, 0); compare(enqueueSpy2.count, 0); compare(replaceAndPlaySpy1.count, 0); compare(replaceAndPlaySpy2.count, 0); compare(goBackSpy1.count, 0); compare(goBackSpy2.count, 0); compare(showArtistSpy1.count, 0); compare(showArtistSpy2.count, 0); var showArtistButtonItem1 = findChild(navigationActionBar1, "showArtistButton"); verify(showArtistButtonItem1 !== null, "valid showArtistButton") mouseClick(showArtistButtonItem1); compare(showArtistSpy1.count, 1); var showArtistButtonItem2 = findChild(navigationActionBar2, "showArtistButton"); verify(showArtistButtonItem2 !== null, "valid showArtistButton") mouseClick(showArtistButtonItem2); compare(showArtistSpy2.count, 0); } function test_filterRating() { persistentSettings.expandedFilterView = true; wait(200); var ratingFilterItem1 = findChild(navigationActionBar1, "ratingFilter"); verify(ratingFilterItem1 !== null, "valid ratingFilter") mouseClick(ratingFilterItem1,1); compare(navigationActionBar1.filterRating, 2); mouseClick(ratingFilterItem1,1); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 4); mouseClick(ratingFilterItem1,1 + elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + 2 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 6); mouseClick(ratingFilterItem1,1 + 2 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + 3 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 8); mouseClick(ratingFilterItem1,1 + 3 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); mouseClick(ratingFilterItem1,1 + 4 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 10); mouseClick(ratingFilterItem1,1 + 4 * elisaTheme.ratingStarSize); compare(navigationActionBar1.filterRating, 0); var ratingFilterItem2 = findChild(navigationActionBar2, "ratingFilter"); verify(ratingFilterItem2 !== null, "valid ratingFilter") mouseClick(ratingFilterItem2,1); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + 2 * elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + 3 * elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); mouseClick(ratingFilterItem2,1 + 4 * elisaTheme.ratingStarSize); compare(navigationActionBar2.filterRating, 0); } function test_filterText() { navigationActionBar1.expandedFilterView = true navigationActionBar2.expandedFilterView = false wait(300) var textsFilterItem1 = findChild(navigationActionBar1, "filterTextInput"); verify(textsFilterItem1 !== null, "valid filterTextInput") textsFilterItem1.focus = false compare(textsFilterItem1.focus, false); mouseClick(textsFilterItem1); compare(textsFilterItem1.focus, true); keyClick(Qt.Key_T); keyClick(Qt.Key_E); keyClick(Qt.Key_S); keyClick(Qt.Key_T); compare(navigationActionBar1.filterText, 'test'); wait(300) mouseClick(textsFilterItem1,textsFilterItem1.width - 20); compare(navigationActionBar1.filterText, ""); } } } diff --git a/autotests/qmltests/tst_PlayListEntry.qml b/autotests/qmltests/tst_PlayListEntry.qml index 2a1e7083..11eaf080 100644 --- a/autotests/qmltests/tst_PlayListEntry.qml +++ b/autotests/qmltests/tst_PlayListEntry.qml @@ -1,124 +1,122 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.3 import QtTest 1.0 import "../../src/qml" import org.kde.elisa 1.0 Item { height: 50 width: 300 PlayListEntry { id: playListEntry height: 50 width: 300 isAlternateColor: false isSingleDiscAlbum: false title: "hello" isValid: true isPlaying: MediaPlayList.IsPaused isSelected: true containsMouse: false function i18nc() { return "" } SystemPalette { id: myPalette } Item { id: elisaTheme property int layoutHorizontalMargin: 8 - property int ratingStarSize: 15 property int playListDelegateHeight: 28 - property int smallControlButtonSize: 22 } } TestCase { name: "TestPlayListEntry" SignalSpy { id: startPlaybackSpy target: playListEntry signalName: "startPlayback" } SignalSpy { id: pausePlaybackSpy target: playListEntry signalName: "pausePlayback" } SignalSpy { id: removeFromPlaylistSpy target: playListEntry signalName: "removeFromPlaylist" } SignalSpy { id: switchToTrackSpy target: playListEntry signalName: "switchToTrack" } when: windowShown function init() { startPlaybackSpy.clear(); pausePlaybackSpy.clear(); removeFromPlaylistSpy.clear(); switchToTrackSpy.clear(); } function test_playnow_click() { compare(startPlaybackSpy.count, 0); compare(pausePlaybackSpy.count, 0); compare(removeFromPlaylistSpy.count, 0); compare(switchToTrackSpy.count, 0); playListEntry.forceActiveFocus(); compare(startPlaybackSpy.count, 0); compare(pausePlaybackSpy.count, 0); compare(removeFromPlaylistSpy.count, 0); compare(switchToTrackSpy.count, 0); var playPauseButtonItem = findChild(playListEntry, "playPauseButton"); verify(playPauseButtonItem !== null, "valid playPauseButton") mouseMove(playPauseButtonItem, playPauseButtonItem.width / 2, playPauseButtonItem.height / 2); wait(300); mouseClick(playPauseButtonItem); compare(startPlaybackSpy.count, 1); compare(pausePlaybackSpy.count, 0); compare(removeFromPlaylistSpy.count, 0); compare(switchToTrackSpy.count, 1); } } } diff --git a/src/qml/BaseTheme.qml b/src/qml/BaseTheme.qml index 3b35f203..a51e3dab 100644 --- a/src/qml/BaseTheme.qml +++ b/src/qml/BaseTheme.qml @@ -1,77 +1,74 @@ /* * 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 import QtQuick.Controls 2.2 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 layoutHorizontalMargin: 8 property int layoutVerticalMargin: 6 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 ratingStarSize: 15 - property int mediaPlayerControlHeight: 42 property real mediaPlayerControlOpacity: 0.6 - property int smallControlButtonSize: 22 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 Label { id: fontSize } } diff --git a/src/qml/FlatButtonWithToolTip.qml b/src/qml/FlatButtonWithToolTip.qml index 6babd8fb..0e8ba635 100644 --- a/src/qml/FlatButtonWithToolTip.qml +++ b/src/qml/FlatButtonWithToolTip.qml @@ -1,46 +1,47 @@ /* * 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. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import QtQuick.Controls 2.3 +import org.kde.kirigami 2.5 as Kirigami import org.kde.elisa 1.0 Button { id: flatButtonWithToolTip activeFocusOnTab: true Keys.onReturnPressed: flatButtonWithToolTip.clicked() Accessible.onPressAction: flatButtonWithToolTip.clicked() flat: true display: AbstractButton.IconOnly - icon.width: elisaTheme.smallControlButtonSize - icon.height: elisaTheme.smallControlButtonSize + icon.width: Kirigami.Units.iconSizes.smallMedium + icon.height: Kirigami.Units.iconSizes.smallMedium implicitWidth: icon.width + 16 implicitHeight: icon.height + 16 ToolTip.visible: hovered ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval ToolTip.text: flatButtonWithToolTip.text } diff --git a/src/qml/HeaderBar.qml b/src/qml/HeaderBar.qml index 5aee6dfb..9b3de9a0 100644 --- a/src/qml/HeaderBar.qml +++ b/src/qml/HeaderBar.qml @@ -1,428 +1,427 @@ /* * 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 QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 FocusScope { id: headerBar property string title property string artist property string albumArtist property string album property string image property string newImage property string oldImage property string tracksCount property int trackRating property int albumID property bool ratingVisible property alias playerControl: playControlItem property alias isMaximized: playControlItem.isMaximized property int imageSourceSize: 512 signal openArtist() signal openAlbum() signal openNowPlaying() onImageChanged: { if (changeBackgroundTransition.running) { changeBackgroundTransition.complete() } newImage = image changeBackgroundTransition.start() } Item { id: background anchors.fill: parent ImageWithFallback { id: oldBackground source: oldImage fallback: elisaTheme.defaultBackgroundImage asynchronous: true anchors.fill: parent fillMode: Image.PreserveAspectCrop sourceSize.width: imageSourceSize opacity: 1 layer.enabled: true layer.effect: HueSaturation { cached: true lightness: -0.5 saturation: 0.9 layer.enabled: true layer.effect: GaussianBlur { cached: true radius: 256 deviation: 12 samples: 129 transparentBorder: false } } } ImageWithFallback { id: newBackground source: newImage fallback: Qt.resolvedUrl(elisaTheme.defaultBackgroundImage) asynchronous: true anchors.fill: parent fillMode: Image.PreserveAspectCrop sourceSize.width: imageSourceSize visible: false opacity: 0 layer.enabled: true layer.effect: HueSaturation { cached: true lightness: -0.5 saturation: 0.9 layer.enabled: true layer.effect: GaussianBlur { cached: true radius: 256 deviation: 12 samples: 129 transparentBorder: false } } } } MediaPlayerControl { id: playControlItem focus: true anchors.left: background.left anchors.right: background.right anchors.bottom: background.bottom height: elisaTheme.mediaPlayerControlHeight } ColumnLayout { id: contentZone anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.bottom: playControlItem.top spacing: 0 RowLayout { spacing: 0 Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true Layout.fillHeight: true Item { Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.preferredHeight: contentZone.height * 0.9 Layout.minimumHeight: contentZone.height * 0.9 Layout.maximumHeight: contentZone.height * 0.9 Layout.preferredWidth: contentZone.height * 0.9 Layout.minimumWidth: contentZone.height * 0.9 Layout.maximumWidth: contentZone.height * 0.9 Layout.leftMargin: !LayoutMirroring.enabled ? contentZone.width * 0.15 : 0 Layout.rightMargin: LayoutMirroring.enabled ? contentZone.width * 0.15 : 0 ImageWithFallback { id: oldMainIcon anchors.fill: parent asynchronous: true mipmap: true source: oldImage fallback: Qt.resolvedUrl(elisaTheme.defaultAlbumImage) sourceSize { width: imageSourceSize height: imageSourceSize } fillMode: Image.PreserveAspectFit } ImageWithFallback { id: newMainIcon anchors.fill: parent asynchronous: true mipmap: true source: newImage fallback: Qt.resolvedUrl(elisaTheme.defaultAlbumImage) visible: false opacity: 0 sourceSize { width: imageSourceSize height: imageSourceSize } fillMode: Image.PreserveAspectFit } } ColumnLayout { spacing: 0 Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.preferredHeight: contentZone.height * 0.9 Layout.minimumHeight: contentZone.height * 0.9 Layout.maximumHeight: contentZone.height * 0.9 Layout.fillWidth: true LabelWithToolTip { id: mainLabel text: title Layout.alignment: Qt.AlignLeft elide: Text.ElideRight // Hardcoded because the headerbar blur always makes a dark-ish // background, so we don't want to use a color scheme color that // might also be dark color: "white" level: 1 font.bold: true Layout.bottomMargin: albumLabel.height * 0.5 MouseArea { id: titleMouseArea hoverEnabled: true anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { openNowPlaying() } } } LabelWithToolTip { id: authorLabel text: artist Layout.alignment: Qt.AlignLeft elide: Text.ElideRight // Hardcoded because the headerbar blur always makes a dark-ish // background, so we don't want to use a color scheme color that // might also be dark color: "white" level: 3 MouseArea { id: authorMouseArea hoverEnabled: true anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { openArtist() } } } LabelWithToolTip { id: albumLabel text: album Layout.alignment: Qt.AlignLeft elide: Text.ElideRight // Hardcoded because the headerbar blur always makes a dark-ish // background, so we don't want to use a color scheme color that // might also be dark color: "white" level: 3 MouseArea { id: albumMouseArea hoverEnabled: true anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { openAlbum() } } } RatingStar { id: mainRating visible: ratingVisible - starSize: elisaTheme.ratingStarSize starRating: trackRating Layout.alignment: Qt.AlignLeft } Loader { active: headerBar.isMaximized visible: headerBar.isMaximized sourceComponent: SimplePlayListView { id: playList playListModel: elisa.mediaPlayList anchors.fill: parent } Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.topMargin: elisaTheme.layoutHorizontalMargin * 6 Layout.bottomMargin: elisaTheme.layoutHorizontalMargin * 2 Layout.leftMargin: elisaTheme.layoutHorizontalMargin * 7 Layout.rightMargin: elisaTheme.layoutHorizontalMargin * 2 } } } } LabelWithToolTip { id: remainingTracksLabel text: tracksCount > 0 ? i18np("1 track remaining", "%1 tracks remaining", tracksCount) : i18n("No remaining tracks") elide: Text.ElideRight visible: tracksCount >= 0 // Hardcoded because the headerbar blur always makes a dark-ish // background, so we don't want to use a color scheme color that // might also be dark color: "white" anchors.right: contentZone.right anchors.bottom: contentZone.bottom anchors.rightMargin: elisaTheme.layoutHorizontalMargin * 2 anchors.bottomMargin: elisaTheme.layoutHorizontalMargin * 2 } SequentialAnimation { id: changeBackgroundTransition PropertyAction { targets: [newBackground, newMainIcon] property: 'opacity' value: 0 } PropertyAction { targets: [newBackground, newMainIcon] property: 'visible' value: true } PropertyAction { target: newBackground property: "source" value: (newImage ? newImage : Qt.resolvedUrl(elisaTheme.defaultBackgroundImage)) } PropertyAction { target: newMainIcon property: "source" value: (newImage ? newImage : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) } ParallelAnimation { NumberAnimation { targets: [newBackground, newMainIcon, mainLabel, authorLabel, remainingTracksLabel, albumLabel] property: 'opacity' from: 0 to: 1 duration: 250 easing.type: Easing.Linear } NumberAnimation { targets: [oldBackground, oldMainIcon] property: 'opacity' from: 1 to: 0 duration: 250 easing.type: Easing.Linear } } PropertyAction { target: headerBar property: "oldImage" value: image } PropertyAction { target: oldBackground property: 'source' value: (headerBar.oldImage ? headerBar.oldImage : Qt.resolvedUrl(elisaTheme.defaultBackgroundImage)) } PropertyAction { target: oldMainIcon property: 'source' value: (headerBar.oldImage ? headerBar.oldImage : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) } PropertyAction { targets: [oldBackground, oldMainIcon] property: 'opacity' value: 1 } PropertyAction { targets: [newBackground, newMainIcon] property: 'visible' value: false } onStopped: { oldImage = newImage } } } diff --git a/src/qml/ListBrowserDelegate.qml b/src/qml/ListBrowserDelegate.qml index 4846996b..20de6400 100644 --- a/src/qml/ListBrowserDelegate.qml +++ b/src/qml/ListBrowserDelegate.qml @@ -1,423 +1,421 @@ /* * Copyright 2016-2017 Matthieu Gallien * Copyright 2017 Alexander Stippich * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: mediaTrack property url trackUrl property var dataType property string title property string artist property string album property string albumArtist property string duration property url imageUrl property int trackNumber property int discNumber property int rating property bool hideDiscNumber property bool isSelected property bool isAlternateColor property bool detailedView: true signal clicked() signal enqueue(var url, var entryType, var name) signal replaceAndPlay(var url, var entryType, var name) signal callOpenMetaDataView(var url, var entryType) Accessible.role: Accessible.ListItem Accessible.name: title Accessible.description: title Keys.onReturnPressed: enqueue(trackUrl, dataType, title) Keys.onEnterPressed: enqueue(trackUrl, dataType, title) TextMetrics { id: mainLabelSize font: mainLabel.font text: mainLabel.text } property int singleLineHeight: elisaTheme.layoutVerticalMargin * 2 + mainLabelSize.height height: singleLineHeight + (detailedView ? mainLabelSize.height : 0) Rectangle { id: rowRoot anchors.fill: parent z: 1 color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) } MouseArea { id: hoverArea anchors.fill: parent z: 2 hoverEnabled: true acceptedButtons: Qt.LeftButton onClicked: { mediaTrack.clicked() } onDoubleClicked: enqueue(trackUrl, dataType, title) RowLayout { anchors.fill: parent spacing: 0 LabelWithToolTip { id: mainLabel visible: !detailedView text: { if (trackNumber !== 0 && trackNumber !== -1 && trackNumber !== undefined) { if (albumArtist !== undefined && artist !== albumArtist) return i18nc("%1: track number. %2: track title. %3: artist name", "%1 - %2 - %3", trackNumber.toLocaleString(Qt.locale(), 'f', 0), title, artist); else return i18nc("%1: track number. %2: track title.", "%1 - %2", trackNumber.toLocaleString(Qt.locale(), 'f', 0), title); } else { if (albumArtist !== undefined && artist !== albumArtist) return i18nc("%1: track title. %2: artist name", "%1 - %2", title, artist); else return i18nc("%1: track title", "%1", 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 (!hideDiscNumber ? elisaTheme.layoutHorizontalMargin * 4 : elisaTheme.layoutHorizontalMargin) else return 0 } Layout.rightMargin: { if (LayoutMirroring.enabled) return (!hideDiscNumber ? elisaTheme.layoutHorizontalMargin * 4 : elisaTheme.layoutHorizontalMargin) else return 0 } } ImageWithFallback { id: coverImageElement Layout.preferredHeight: mediaTrack.height - elisaTheme.layoutVerticalMargin Layout.preferredWidth: mediaTrack.height - elisaTheme.layoutVerticalMargin Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutVerticalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutVerticalMargin : 0 Layout.alignment: Qt.AlignCenter visible: detailedView sourceSize.width: mediaTrack.height - elisaTheme.layoutVerticalMargin sourceSize.height: mediaTrack.height - elisaTheme.layoutVerticalMargin fillMode: Image.PreserveAspectFit smooth: true source: imageUrl fallback: elisaTheme.defaultAlbumImage asynchronous: true layer.enabled: !usingFallback 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 level: 4 text: { if (trackNumber >= 0) { return i18nc("%1: track number. %2: track title", "%1 - %2", trackNumber.toLocaleString(Qt.locale(), 'f', 0), title); } else { return title; } } horizontalAlignment: Text.AlignLeft 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: { var labelText = "" if (artist) { labelText += artist } if (album !== '') { labelText += ' - ' + album if (!hideDiscNumber && discNumber !== -1) { labelText += ' - CD ' + discNumber } } return labelText; } horizontalAlignment: Text.AlignLeft opacity: 0.6 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 Layout.rightMargin: 10 z: 1 opacity: 0 sourceComponent: Row { anchors.centerIn: parent FlatButtonWithToolTip { id: detailsButton height: singleLineHeight width: singleLineHeight text: i18nc("Show track metadata", "View Details") icon.name: "help-about" onClicked: callOpenMetaDataView(trackUrl, dataType) } FlatButtonWithToolTip { id: enqueueButton height: singleLineHeight width: singleLineHeight text: i18nc("Enqueue current track", "Enqueue") icon.name: "list-add" onClicked: enqueue(trackUrl, dataType, title) } FlatButtonWithToolTip { id: clearAndEnqueueButton scale: LayoutMirroring.enabled ? -1 : 1 height: singleLineHeight width: singleLineHeight text: i18nc("Clear play list and enqueue current track", "Play Now and Replace Play List") icon.name: "media-playback-start" onClicked: replaceAndPlay(trackUrl, dataType, title) } } } RatingStar { id: ratingWidget - starSize: elisaTheme.ratingStarSize - starRating: rating Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin } LabelWithToolTip { id: durationLabel text: 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 } } } states: [ State { name: 'notSelected' when: !mediaTrack.activeFocus && !hoverArea.containsMouse && !mediaTrack.isSelected PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 0.0 } PropertyChanges { target: rowRoot color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) } PropertyChanges { target: rowRoot opacity: 1 } }, State { name: 'hovered' when: !mediaTrack.activeFocus && hoverArea.containsMouse PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: hoverLoader opacity: 1.0 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } PropertyChanges { target: rowRoot color: myPalette.highlight } PropertyChanges { target: rowRoot opacity: 0.2 } }, State { name: 'selected' when: !mediaTrack.activeFocus && mediaTrack.isSelected PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } PropertyChanges { target: rowRoot color: myPalette.mid } PropertyChanges { target: rowRoot opacity: 1. } }, State { name: 'focused' when: mediaTrack.activeFocus PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: hoverLoader opacity: 1.0 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } PropertyChanges { target: rowRoot color: myPalette.highlight } PropertyChanges { target: rowRoot opacity: 0.6 } } ] } diff --git a/src/qml/MetaDataDelegate.qml b/src/qml/MetaDataDelegate.qml index dbc21a9d..4a6f9e0a 100644 --- a/src/qml/MetaDataDelegate.qml +++ b/src/qml/MetaDataDelegate.qml @@ -1,132 +1,131 @@ /* * 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 QtQuick 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import org.kde.elisa 1.0 RowLayout { id: delegateRow spacing: 0 height: (model.type === EditableTrackMetadataModel.LongTextEntry ? longTextDisplayLoader.height : (metaDataLabelMetric.boundingRect.height + elisaTheme.layoutVerticalMargin / 2)) TextMetrics { id: metaDataLabelMetric text: 'Metadata Name' } Label { id: metaDataLabels text: i18nc("Label for a piece of metadata, e.g. 'Album Artist:'", "%1:", model.name) font.weight: Font.Bold horizontalAlignment: Text.AlignRight Layout.alignment: Qt.AlignTop Layout.preferredWidth: 0.8 * elisaTheme.coverImageSize Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 } Loader { id: textDisplayLoader active: model.type === EditableTrackMetadataModel.TextEntry || model.type === EditableTrackMetadataModel.IntegerEntry visible: model.type === EditableTrackMetadataModel.TextEntry || model.type === EditableTrackMetadataModel.IntegerEntry Layout.fillWidth: true Layout.alignment: Qt.AlignTop sourceComponent: LabelWithToolTip { text: model.display horizontalAlignment: Text.AlignLeft elide: Text.ElideRight anchors.fill: parent } } Loader { id: longTextDisplayLoader active: model.type === EditableTrackMetadataModel.LongTextEntry visible: model.type === EditableTrackMetadataModel.LongTextEntry Layout.fillWidth: true Layout.maximumWidth: delegateRow.width - (0.8 * elisaTheme.coverImageSize + elisaTheme.layoutHorizontalMargin * 2) Layout.alignment: Qt.AlignTop sourceComponent: Label { text: model.display horizontalAlignment: Text.AlignLeft elide: Text.ElideRight anchors.fill: parent wrapMode: Text.WordWrap } } Loader { active: model.type === EditableTrackMetadataModel.DateEntry visible: model.type === EditableTrackMetadataModel.DateEntry Layout.fillWidth: true Layout.alignment: Qt.AlignTop sourceComponent: LabelWithToolTip { text: rawDate.toLocaleDateString() horizontalAlignment: Text.AlignLeft elide: Text.ElideRight anchors.fill: parent property date rawDate: new Date(model.display) } } Loader { active: model.type === EditableTrackMetadataModel.RatingEntry visible: model.type === EditableTrackMetadataModel.RatingEntry Layout.fillWidth: true Layout.alignment: Qt.AlignTop sourceComponent: RatingStar { starRating: model.display - starSize: elisaTheme.ratingStarSize readOnly: true anchors { left: parent.left top: parent.top bottom: parent.bottom } } } } diff --git a/src/qml/NavigationActionBar.qml b/src/qml/NavigationActionBar.qml index 2141aed8..1b3be24c 100644 --- a/src/qml/NavigationActionBar.qml +++ b/src/qml/NavigationActionBar.qml @@ -1,240 +1,239 @@ /* * 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: elisaTheme.layoutHorizontalMargin 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: elisaTheme.layoutHorizontalMargin 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" 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: "" Accessible.role: Accessible.EditableText placeholderText: i18n("Search for album name, artist, etc.") Keys.onEscapePressed: persistentSettings.expandedFilterView = false; }, Item { width: elisaTheme.layoutHorizontalMargin }, LabelWithToolTip { text: i18n("Filter by rating: ") visible: showRating }, RatingStar { id: ratingFilter objectName: 'ratingFilter' visible: showRating hoverWidgetOpacity: 1 readOnly: false - starSize: elisaTheme.ratingStarSize } ] } 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: 250 } } } diff --git a/src/qml/PlayListEntry.qml b/src/qml/PlayListEntry.qml index 82afc2e5..bdccaebe 100644 --- a/src/qml/PlayListEntry.qml +++ b/src/qml/PlayListEntry.qml @@ -1,394 +1,393 @@ /* * 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.Layouts 1.2 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 +import org.kde.kirigami 2.5 as Kirigami 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 int databaseId: 0 property var entryType property string title property string artist property string album property string albumArtist property string duration property url fileName property url imageUrl property int trackNumber property int discNumber property int rating property bool hasValidDiscNumber: true property int scrollBarWidth property bool simpleMode: false signal startPlayback() signal pausePlayback() signal removeFromPlaylist(var trackIndex) signal switchToTrack(var trackIndex) Accessible.role: Accessible.ListItem Accessible.name: title + ' ' + album + ' ' + artist TextMetrics { id: mainCompactLabelSize font: mainCompactLabel.font text: mainCompactLabel.text } Keys.onReturnPressed: { playListEntry.switchToTrack(playListEntry.index) playListEntry.startPlayback() } height: mainCompactLabelSize.height + 2 * elisaTheme.layoutVerticalMargin Loader { id: metadataLoader active: false onLoaded: item.show() sourceComponent: MediaTrackMetadataView { fileName: playListEntry.fileName showImage: entryType !== ElisaUtils.Radio modelType: entryType showTrackFileName: entryType !== ElisaUtils.Radio showDeleteButton: entryType === ElisaUtils.Radio showApplyButton: entryType === ElisaUtils.Radio editableMetadata: entryType === ElisaUtils.Radio onRejected: metadataLoader.active = false } } Rectangle { id: entryBackground anchors.fill: parent anchors.rightMargin: LayoutMirroring.enabled ? scrollBarWidth : 0 z: 1 color: simpleMode ? "transparent" : myPalette.base height: playListEntry.height } RowLayout { id: trackRow z: 2 anchors.fill: parent anchors.leftMargin: elisaTheme.layoutHorizontalMargin anchors.rightMargin: LayoutMirroring.enabled ? scrollBarWidth : 0 spacing: elisaTheme.layoutHorizontalMargin / 4 // Container for the play/pause icon and the track/disc label Item { TextMetrics { id: fakeLabel text: '99/9' } Layout.minimumWidth: fakeLabel.width Layout.preferredWidth: fakeLabel.width Layout.maximumWidth: fakeLabel.width Layout.preferredHeight: playListEntry.height Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 Image { id: playIcon anchors.centerIn: parent source: (isPlaying === MediaPlayList.IsPlaying ? Qt.resolvedUrl(elisaTheme.playingIndicatorIcon) : Qt.resolvedUrl(elisaTheme.pausedIndicatorIcon)) - width: elisaTheme.smallControlButtonSize - height: elisaTheme.smallControlButtonSize - sourceSize.width: elisaTheme.smallControlButtonSize - sourceSize.height: elisaTheme.smallControlButtonSize + width: Kirigami.Units.iconSizes.smallMedium + height: Kirigami.Units.iconSizes.smallMedium + sourceSize.width: Kirigami.Units.iconSizes.smallMedium + sourceSize.height: Kirigami.Units.iconSizes.smallMedium fillMode: Image.PreserveAspectFit mirror: LayoutMirroring.enabled layer.enabled: simpleMode layer.effect: ColorOverlay { cached: true color: myPalette.highlightedText } visible: isPlaying === MediaPlayList.IsPlaying || isPlaying === MediaPlayList.IsPaused } Label { id: trackAndDiscNumberLabel anchors.fill: parent anchors.rightMargin: LayoutMirroring.enabled ? 0 : elisaTheme.layoutHorizontalMargin anchors.leftMargin: !LayoutMirroring.enabled ? 0 : elisaTheme.layoutHorizontalMargin horizontalAlignment: Text.AlignRight text: { var trackNumberString; if (trackNumber !== -1) { trackNumberString = Number(trackNumber).toLocaleString(Qt.locale(), 'f', 0); } else { trackNumberString = '' } if (!isSingleDiscAlbum && discNumber !== 0 ) { return trackNumberString + "/" + Number(discNumber).toLocaleString(Qt.locale(), 'f', 0) } else { return trackNumberString } } font.weight: (isPlaying ? Font.Bold : Font.Light) color: simpleMode ? myPalette.highlightedText : myPalette.text visible: isValid && !playIcon.visible } } LabelWithToolTip { id: mainCompactLabel text: title font.weight: (isPlaying ? Font.Bold : Font.Normal) color: simpleMode ? myPalette.highlightedText : 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: title color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft visible: !isValid elide: Text.ElideRight } Item { Layout.fillWidth: true Layout.preferredWidth: 0 } Loader { id: hoverLoader active: false visible: active Layout.alignment: Qt.AlignVCenter | Qt.AlignRight sourceComponent: Row { anchors.centerIn: parent enabled: isValid FlatButtonWithToolTip { id: infoButton objectName: 'infoButton' implicitHeight: playListEntry.height implicitWidth: playListEntry.height text: i18nc("Show track metadata", "View Details") icon.name: "help-about" onClicked: { if (metadataLoader.active === false) { metadataLoader.active = true } else { metadataLoader.item.close(); metadataLoader.active = false } } } FlatButtonWithToolTip { id: playPauseButton objectName: 'playPauseButton' implicitHeight: playListEntry.height implicitWidth: playListEntry.height scale: LayoutMirroring.enabled ? -1 : 1 // We can mirror the symmetrical pause icon text: (isPlaying === MediaPlayList.IsPlaying) ? i18nc("Pause current track from play list", "Pause") : i18nc("Play this track from play list", "Play") icon.name: (isPlaying === MediaPlayList.IsPlaying) ? "media-playback-pause" : "media-playback-start" onClicked: if (isPlaying === MediaPlayList.IsPlaying) { playListEntry.pausePlayback() } else { playListEntry.switchToTrack(playListEntry.index) playListEntry.startPlayback() } } FlatButtonWithToolTip { id: removeButton objectName: 'removeButton' implicitHeight: playListEntry.height implicitWidth: playListEntry.height text: i18nc("Remove current track from play list", "Remove") icon.name: "error" onClicked: playListEntry.removeFromPlaylist(playListEntry.index) } } } RatingStar { id: ratingWidget starRating: rating - starSize: elisaTheme.ratingStarSize - visible: rating > 0 } LabelWithToolTip { id: durationLabel text: duration font.weight: (isPlaying ? Font.Bold : Font.Normal) color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin horizontalAlignment: Text.AlignRight } } states: [ State { name: 'notSelected' when: !containsMouse && !isSelected && !playListEntry.activeFocus && !simpleMode PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: entryBackground color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) } PropertyChanges { target: entryBackground opacity: 1. } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 0.0 } }, State { name: 'hovered' when: containsMouse && !playListEntry.activeFocus && !simpleMode PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: entryBackground color: myPalette.highlight } PropertyChanges { target: entryBackground opacity: 0.2 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } }, State { name: 'selected' when: !playListEntry.activeFocus && isSelected && !simpleMode PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: entryBackground color: myPalette.mid } PropertyChanges { target: entryBackground opacity: 1. } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } }, State { name: 'focused' when: playListEntry.activeFocus && !simpleMode PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: entryBackground color: myPalette.highlight } PropertyChanges { target: entryBackground opacity: 0.6 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } } ] } diff --git a/src/qml/RatingStar.qml b/src/qml/RatingStar.qml index d8c14cf2..bcfecfee 100644 --- a/src/qml/RatingStar.qml +++ b/src/qml/RatingStar.qml @@ -1,89 +1,89 @@ /* * 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 QtQuick 2.7 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 +import org.kde.kirigami 2.5 as Kirigami Row { - property int starSize property int starRating property bool readOnly: true property double hoverBrightness: 0.5 property double hoverContrast: 0.5 property double hoverWidgetOpacity: 0 property int hoveredRating: 0 spacing: 0 opacity: starRating > 0 ? 1 : (readOnly ? 0 : hoverWidgetOpacity) Repeater { model: 5 Item { property int ratingThreshold: 2 + index * 2 - height: starSize - width: starSize + height: Kirigami.Units.iconSizes.small + width: Kirigami.Units.iconSizes.small Image { - width: starSize - height: starSize + width: Kirigami.Units.iconSizes.small + height: Kirigami.Units.iconSizes.small anchors.centerIn: parent - sourceSize.width: starSize - sourceSize.height: starSize + sourceSize.width: Kirigami.Units.iconSizes.small + sourceSize.height: Kirigami.Units.iconSizes.small fillMode: Image.PreserveAspectFit layer.enabled: hoveredRating >= ratingThreshold layer.effect: BrightnessContrast { brightness: hoverBrightness contrast: hoverContrast } source: if (starRating >= ratingThreshold || hoveredRating >= ratingThreshold) Qt.resolvedUrl(elisaTheme.ratingIcon) else Qt.resolvedUrl(elisaTheme.ratingUnratedIcon) opacity: if (starRating >= ratingThreshold || hoveredRating >= ratingThreshold) 1 else 0.7 } MouseArea { anchors.fill: parent enabled: !readOnly acceptedButtons: Qt.LeftButton hoverEnabled: true onClicked: if (starRating !== ratingThreshold) { starRating = ratingThreshold } else { starRating = 0 } onEntered: hoveredRating = ratingThreshold onExited: hoveredRating = 0 } } } }