diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index 623fb203..08ccc915 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,569 +1,571 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.elisa 1.0 RowLayout { id: contentViewContainer spacing: 0 property bool showPlaylist property alias currentViewIndex: listViews.currentIndex signal toggleSearch() function goBack() { viewManager.goBack() } function openArtist(name) { viewManager.openChildView(name, '', elisaTheme.artistIcon, 0, ElisaUtils.Artist) } function openAlbum(album, artist, image, albumID) { image = !image ? elisaTheme.defaultAlbumImage : image; viewManager.openChildView(album, artist, image, albumID, ElisaUtils.Album); } function openNowPlaying() { viewManager.closeAllViews(); } ViewManager { id: viewManager onSwitchOffAllViews: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > 1) { browseStackView.pop() } } onSwitchRecentlyPlayedTracksView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(allRecentlyPlayedTracksView, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, modelType: dataType, stackView: browseStackView, opacity: 0, }) } onSwitchFrequentlyPlayedTracksView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(allFrequentlyPlayedTracksView, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, modelType: dataType, stackView: browseStackView, opacity: 0, }) } onOpenGridView: { if (expectedDepth === 1) { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) } while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(dataGridView, { viewType: viewType, mainTitle: pageModel.viewMainTitle(viewType, mainTitle), secondaryTitle: secondaryTitle, image: pageModel.viewImageUrl(viewType, imageUrl), modelType: dataType, defaultIcon: viewDefaultIcon, showRating: viewShowRating, delegateDisplaySecondaryText: viewDelegateDisplaySecondaryText, genreFilterText: genreNameFilter, artistFilter: artistNameFilter, isSubPage: (browseStackView.depth >= 2), stackView: browseStackView, opacity: 0, }) } onSwitchOneAlbumView: { while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(albumView, { viewType: viewType, mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, databaseId: databaseId, stackView: browseStackView, opacity: 0, }) } onSwitchAllTracksView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(allTracksView, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, modelType: dataType, stackView: browseStackView, opacity: 0, }) } onSwitchFilesBrowserView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(filesBrowserView, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, opacity: 0, }) } onPopOneView: { if (browseStackView.depth > 2) { browseStackView.pop() } } } ViewsModel { id: pageModel } ViewSelector { id: listViews model: pageModel Layout.fillHeight: true Behavior on Layout.maximumWidth { NumberAnimation { duration: 150 } } onSwitchView: viewManager.openParentView(viewType, pageModel.viewMainTitle(viewType, ""), pageModel.viewImageUrl(viewType, "")) } 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 MouseArea { anchors.fill: parent acceptedButtons: Qt.BackButton onClicked: goBack() } Rectangle { border { color: (mainContentView.activeFocus ? myPalette.highlight : myPalette.base) width: 1 } radius: 3 color: myPalette.base anchors.fill: parent StackView { id: browseStackView anchors.fill: parent clip: true initialItem: Item { } popEnter: Transition { OpacityAnimator { from: 0.0 to: 1.0 duration: 300 } } popExit: Transition { OpacityAnimator { from: 1.0 to: 0.0 duration: 300 } } pushEnter: Transition { OpacityAnimator { from: 0.0 to: 1.0 duration: 300 } } pushExit: Transition { OpacityAnimator { from: 1.0 to: 0.0 duration: 300 } } replaceEnter: Transition { OpacityAnimator { from: 0.0 to: 1.0 duration: 300 } } replaceExit: Transition { OpacityAnimator { from: 1.0 to: 0.0 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 Layout.fillHeight: true - Layout.leftMargin: elisaTheme.layoutHorizontalMargin + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 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 } Loader { id: albumContext active: Layout.minimumWidth != 0 sourceComponent: ContextView { anchors.fill: parent databaseId: elisa.manageHeaderBar.databaseId title: elisa.manageHeaderBar.title artistName: elisa.manageHeaderBar.artist albumName: elisa.manageHeaderBar.album albumArtUrl: elisa.manageHeaderBar.image fileUrl: elisa.manageHeaderBar.fileName } Layout.fillHeight: true Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 - Layout.leftMargin: elisaTheme.layoutHorizontalMargin * 1.5 + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin * 1.5 : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin * 1.5 : 0 visible: Layout.minimumWidth != 0 } } } states: [ State { name: 'playList' when: listViews.currentIndex === 0 PropertyChanges { target: mainContentView 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 / 5 + elisaTheme.layoutHorizontalMargin Layout.maximumWidth: contentZone.width * 2 / 5 + elisaTheme.layoutHorizontalMargin Layout.preferredWidth: contentZone.width * 2 / 5 + elisaTheme.layoutHorizontalMargin } PropertyChanges { target: viewSeparatorItem Layout.minimumWidth: 1 Layout.maximumWidth: 1 Layout.preferredWidth: 1 } PropertyChanges { target: albumContext Layout.minimumWidth: contentZone.width * 3 / 5 - 2 - 3.5 * elisaTheme.layoutHorizontalMargin Layout.maximumWidth: contentZone.width * 3 / 5 - 2 - 3.5 * elisaTheme.layoutHorizontalMargin Layout.preferredWidth: contentZone.width * 3 / 5 - 2 - 3.5 * elisaTheme.layoutHorizontalMargin } }, State { name: "browsingViewsNoPlaylist" when: listViews.currentIndex !== 0 && contentViewContainer.showPlaylist !== true extend: "browsingViews" PropertyChanges { target: mainContentView Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width } PropertyChanges { target: playList Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } }, State { name: 'browsingViews' when: listViews.currentIndex !== 0 PropertyChanges { target: mainContentView Layout.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 - 3 * elisaTheme.layoutHorizontalMargin + 2 Layout.maximumWidth: contentZone.width * 0.33 - 3 * elisaTheme.layoutHorizontalMargin + 2 Layout.preferredWidth: contentZone.width * 0.33 - 3 * elisaTheme.layoutHorizontalMargin + 2 } 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: allFrequentlyPlayedTracksView FrequentlyPlayedTracks { StackView.onActivated: viewManager.viewIsLoaded(viewType) } } Component { id: allRecentlyPlayedTracksView RecentlyPlayedTracks { StackView.onActivated: viewManager.viewIsLoaded(viewType) } } Component { id: dataGridView DataGridView { StackView.onActivated: viewManager.viewIsLoaded(viewType) } } Component { id: allTracksView TracksView { StackView.onActivated: viewManager.viewIsLoaded(viewType) } } Component { id: albumView AlbumView { StackView.onActivated: viewManager.viewIsLoaded(viewType) } } Component { id: filesBrowserView FileBrowserView { StackView.onActivated: viewManager.viewIsLoaded(viewType) } } } diff --git a/src/qml/ContextView.qml b/src/qml/ContextView.qml index a3936a35..4a93dd6f 100644 --- a/src/qml/ContextView.qml +++ b/src/qml/ContextView.qml @@ -1,234 +1,235 @@ /* * 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.Window 2.2 import QtQuick.Controls 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import org.kde.elisa 1.0 FocusScope { id: topItem property int databaseId: 0 property alias title: titleLabel.text property string albumName: '' property string artistName: '' property url albumArtUrl: '' property string fileUrl: '' TrackContextMetaDataModel { id: metaDataModel manager: elisa.musicManager } ColumnLayout { anchors.fill: parent spacing: 0 TextMetrics { id: titleHeight text: viewTitleHeight.text font { pointSize: viewTitleHeight.font.pointSize bold: viewTitleHeight.font.bold } } LabelWithToolTip { id: viewTitleHeight text: i18nc("Title of the context view related to the currently playing track", "Now Playing") font.pointSize: elisaTheme.defaultFontPointSize * 2 Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.topMargin: elisaTheme.layoutVerticalMargin * 3 Layout.bottomMargin: titleHeight.height } Image { id: albumIcon source: albumArtUrl.toString() === '' ? Qt.resolvedUrl(elisaTheme.defaultAlbumImage) : albumArtUrl Layout.fillWidth: true Layout.maximumHeight: elisaTheme.contextCoverImageSize Layout.preferredHeight: elisaTheme.contextCoverImageSize + Layout.maximumWidth: topItem.width Layout.bottomMargin: elisaTheme.layoutVerticalMargin - width: elisaTheme.contextCoverImageSize - height: elisaTheme.contextCoverImageSize - - sourceSize.width: parent.width - sourceSize.height: elisaTheme.contextCoverImageSize + sourceSize.width: topItem.width + sourceSize.height: topItem.width asynchronous: true fillMode: Image.PreserveAspectCrop } LabelWithToolTip { id: titleLabel font.pointSize: elisaTheme.defaultFontPointSize * 2 font.weight: Font.Bold Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.topMargin: elisaTheme.layoutVerticalMargin - - elide: Text.ElideRight + Layout.fillWidth: true + Layout.maximumWidth: topItem.width } LabelWithToolTip { id: albumArtistLabel text: (artistName && albumName ? i18nc('display of artist and album in context view', 'by %1 from %2', artistName, albumName) : '') font.pointSize: elisaTheme.defaultFontPointSize * 1.4 visible: artistName !== '' && albumName !== '' Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.bottomMargin: elisaTheme.layoutVerticalMargin - - elide: Text.ElideRight + Layout.fillWidth: true + Layout.maximumWidth: topItem.width } LabelWithToolTip { id: albumLabel text: (albumName ? i18nc('display of album in context view', 'from %1', albumName) : '') font.pointSize: elisaTheme.defaultFontPointSize * 1.4 visible: artistName === '' && albumName !== '' Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.bottomMargin: elisaTheme.layoutVerticalMargin - - elide: Text.ElideRight + Layout.fillWidth: true + Layout.maximumWidth: topItem.width } LabelWithToolTip { id: artistLabel text: (artistName ? i18nc('display of artist in context view', 'by %1', artistName) : '') font.pointSize: elisaTheme.defaultFontPointSize * 1.4 visible: artistName !== '' && albumName === '' Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.bottomMargin: elisaTheme.layoutVerticalMargin - - elide: Text.ElideRight + Layout.fillWidth: true + Layout.maximumWidth: topItem.width } Flickable { id: flickable clip: true contentWidth: topItem.width contentHeight: allMetaData.height Layout.fillWidth: true Layout.fillHeight: true boundsBehavior: Flickable.StopAtBounds ScrollBar.vertical: ScrollBar { id: scrollBar policy: ScrollBar.AlwaysOn } ColumnLayout { id: allMetaData spacing: 0 width: topItem.width Repeater { id: trackData model: metaDataModel delegate: MetaDataDelegate { Layout.fillWidth: true } } ContextViewLyrics { id: lyricsContextView Layout.fillWidth: true visible: metaDataModel.lyrics !== "" lyrics: metaDataModel.lyrics } } } - Row { + RowLayout { Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.bottomMargin: elisaTheme.layoutVerticalMargin + Layout.maximumWidth: topItem.width spacing: elisaTheme.layoutHorizontalMargin Image { sourceSize.width: fileNameLabel.height sourceSize.height: fileNameLabel.height source: elisaTheme.folderIcon } LabelWithToolTip { id: fileNameLabel + Layout.fillWidth: true + text: fileUrl - elide: Text.ElideRight + elide: Text.ElideLeft } } } onDatabaseIdChanged: { metaDataModel.initializeByTrackId(databaseId) } Connections { target: elisa onMusicManagerChanged: { metaDataModel.initializeByTrackId(databaseId) } } Component.onCompleted: { if (elisa.musicManager) { metaDataModel.initializeByTrackId(databaseId) } } } diff --git a/src/qml/LabelWithToolTip.qml b/src/qml/LabelWithToolTip.qml index 34ed4408..1531d1d4 100644 --- a/src/qml/LabelWithToolTip.qml +++ b/src/qml/LabelWithToolTip.qml @@ -1,74 +1,76 @@ /* * 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 import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 Label { id: theLabel horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + Loader { anchors.fill: parent active: theLabel.truncated visible: theLabel.truncated sourceComponent: MouseArea { id: hoverArea anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.NoButton Loader { anchors.fill: parent active: hoverArea && hoverArea.containsMouse sourceComponent: ToolTip { delay: Qt.styleHints.mousePressAndHoldInterval visible: hoverArea && hoverArea.containsMouse && theLabel.truncated text: theLabel.text contentItem: Label { text: theLabel.text 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 } } } } } } }