diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml index 2b3e35a7..3d566362 100644 --- a/src/qml/ElisaMainWindow.qml +++ b/src/qml/ElisaMainWindow.qml @@ -1,290 +1,324 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Controls 1.4 as Controls1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import org.kde.elisa 1.0 import Qt.labs.settings 1.0 ApplicationWindow { id: mainWindow visible: true minimumWidth: contentView.showPlaylist ? 1100 : 700 minimumHeight: 600 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true x: persistentSettings.x y: persistentSettings.y width: persistentSettings.width height: persistentSettings.height title: i18n("Elisa") property var goBackAction: elisa.action("go_back") property var findAction: elisa.action("edit_find") Controls1.Action { shortcut: findAction.shortcut onTriggered: { if ( persistentSettings.expandedFilterView == true) { persistentSettings.expandedFilterView = false } else { persistentSettings.expandedFilterView = true } } } Controls1.Action { shortcut: goBackAction.shortcut onTriggered: contentView.goBack() } Controls1.Action { id: applicationMenuAction text: i18nc("open application menu", "Application Menu") iconName: "application-menu" onTriggered: applicationMenu.popup() } ApplicationMenu { id: applicationMenu } SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } Settings { id: persistentSettings property int x property int y property int width : 1100 property int height : 600 property var playListState property var audioPlayerState property double playControlItemVolume : 100.0 property bool playControlItemMuted : false property bool playControlItemRepeat : false property bool playControlItemShuffle : false property bool expandedFilterView: false property bool showPlaylist: true + + property bool headerBarIsMaximized: false } Connections { target: Qt.application onAboutToQuit: { persistentSettings.x = mainWindow.x; persistentSettings.y = mainWindow.y; persistentSettings.width = mainWindow.width; persistentSettings.height = mainWindow.height; persistentSettings.playListState = elisa.mediaPlayList.persistentState; persistentSettings.audioPlayerState = elisa.audioControl.persistentState persistentSettings.playControlItemVolume = headerBar.playerControl.volume persistentSettings.playControlItemMuted = headerBar.playerControl.muted persistentSettings.playControlItemRepeat = headerBar.playerControl.repeat persistentSettings.playControlItemShuffle = headerBar.playerControl.shuffle persistentSettings.showPlaylist = contentView.showPlaylist + + persistentSettings.headerBarIsMaximized = headerBar.isMaximized } } Loader { id: mprisloader active: false sourceComponent: PlatformIntegration { id: platformInterface playListModel: elisa.mediaPlayList audioPlayerManager: elisa.audioControl player: elisa.audioPlayer headerBarManager: elisa.manageHeaderBar manageMediaPlayerControl: elisa.playerControl onRaisePlayer: { mainWindow.show() mainWindow.raise() mainWindow.requestActivate() } } } Connections { target: elisa.audioPlayer onVolumeChanged: headerBar.playerControl.volume = elisa.audioPlayer.volume onMutedChanged: headerBar.playerControl.muted = elisa.audioPlayer.muted } Connections { target: elisa.mediaPlayList onPlayListLoadFailed: { messageNotification.showNotification(i18nc("message of passive notification when playlist load failed", "Load of playlist failed"), 3000) } } PassiveNotification { id: messageNotification } Rectangle { color: myPalette.base anchors.fill: parent ColumnLayout { anchors.fill: parent spacing: 0 Item { - Layout.preferredHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight + id: headerBarParent Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.fillWidth: true + HeaderBar { id: headerBar focus: true anchors.fill: parent tracksCount: elisa.manageHeaderBar.remainingTracks album: elisa.manageHeaderBar.album title: elisa.manageHeaderBar.title artist: elisa.manageHeaderBar.artist image: elisa.manageHeaderBar.image ratingVisible: false playerControl.duration: elisa.audioPlayer.duration playerControl.seekable: elisa.audioPlayer.seekable playerControl.volume: persistentSettings.playControlItemVolume playerControl.muted: persistentSettings.playControlItemMuted playerControl.position: elisa.audioPlayer.position playerControl.skipBackwardEnabled: elisa.playerControl.skipBackwardControlEnabled playerControl.skipForwardEnabled: elisa.playerControl.skipForwardControlEnabled playerControl.playEnabled: elisa.playerControl.playControlEnabled playerControl.isPlaying: elisa.playerControl.musicPlaying playerControl.repeat: persistentSettings.playControlItemRepeat playerControl.shuffle: persistentSettings.playControlItemShuffle playerControl.onSeek: elisa.audioPlayer.seek(position) playerControl.onPlay: elisa.audioControl.playPause() playerControl.onPause: elisa.audioControl.playPause() playerControl.onPlayPrevious: elisa.mediaPlayList.skipPreviousTrack() playerControl.onPlayNext: elisa.mediaPlayList.skipNextTrack() + playerControl.isMaximized: persistentSettings.headerBarIsMaximized + TrackImportNotification { id: importedTracksCountNotification anchors { right: headerBar.right top: headerBar.top rightMargin: elisaTheme.layoutHorizontalMargin * 1.75 topMargin: elisaTheme.layoutHorizontalMargin * 3 } } Binding { target: importedTracksCountNotification property: 'musicManager' value: elisa.musicManager when: elisa.musicManager !== undefined } Loader { sourceComponent: Binding { target: importedTracksCountNotification property: 'indexingRunning' value: elisa.musicManager.indexingRunning } active: elisa.musicManager !== undefined } Loader { sourceComponent: Binding { target: importedTracksCountNotification property: 'importedTracksCount' value: elisa.musicManager.importedTracksCount } active: elisa.musicManager !== undefined } } } ContentView { id: contentView Layout.fillHeight: true Layout.fillWidth: true showPlaylist: persistentSettings.showPlaylist } } } + StateGroup { + id: mainWindowState + states: [ + State { + name: "headerBarIsMaximized" + when: headerBar.isMaximized + changes: [ + PropertyChanges { + target: mainWindow + minimumHeight: 120 + elisaTheme.mediaPlayerControlHeight + explicit: true + }, + PropertyChanges { + target: headerBarParent + Layout.minimumHeight: mainWindow.height + Layout.maximumHeight: mainWindow.height + }, + PropertyChanges { + target: contentView + height: 0 + visible: false + } + ] + } + ] + } + Component.onCompleted: { elisa.initialize() elisa.mediaPlayList.randomPlay = Qt.binding(function() { return headerBar.playerControl.shuffle }) elisa.mediaPlayList.repeatPlay = Qt.binding(function() { return headerBar.playerControl.repeat }) elisa.playerControl.randomOrContinuePlay = Qt.binding(function() { return headerBar.playerControl.shuffle || headerBar.playerControl.repeat}) if (persistentSettings.playListState) { elisa.mediaPlayList.persistentState = persistentSettings.playListState } if (persistentSettings.audioPlayerState) { elisa.audioControl.persistentState = persistentSettings.audioPlayerState } elisa.audioPlayer.muted = Qt.binding(function() { return headerBar.playerControl.muted }) elisa.audioPlayer.volume = Qt.binding(function() { return headerBar.playerControl.volume }) mprisloader.active = true } } diff --git a/src/qml/HeaderBar.qml b/src/qml/HeaderBar.qml index 58c99747..064fa701 100644 --- a/src/qml/HeaderBar.qml +++ b/src/qml/HeaderBar.qml @@ -1,389 +1,390 @@ /* * Copyright 2016 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 FocusScope { id: headerBar property string title property string artist property string album property string image property string newImage property string oldImage property string tracksCount property int trackRating property bool ratingVisible property alias playerControl: playControlItem + property alias isMaximized: playControlItem.isMaximized onImageChanged: { if (changeBackgroundTransition.running) { changeBackgroundTransition.complete() } newImage = image changeBackgroundTransition.start() } Item { id: background anchors.fill: parent Image { id: oldBackground source: (oldImage ? oldImage : Qt.resolvedUrl(elisaTheme.defaultBackgroundImage)) asynchronous: true anchors.fill: parent fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width 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 } } } Image { id: newBackground source: (newImage ? newImage : Qt.resolvedUrl(elisaTheme.defaultBackgroundImage)) asynchronous: true anchors.fill: parent fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width 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 Item { Layout.fillHeight: true Layout.fillWidth: true } 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 Image { id: oldMainIcon anchors.fill: parent asynchronous: true source: (oldImage ? oldImage : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) sourceSize { width: contentZone.height * 0.9 height: contentZone.height * 0.9 } fillMode: Image.PreserveAspectFit } Image { id: newMainIcon anchors.fill: parent asynchronous: true source: (newImage ? newImage : Qt.resolvedUrl(elisaTheme.defaultAlbumImage)) visible: false opacity: 0 sourceSize { width: contentZone.height * 0.9 height: contentZone.height * 0.9 } fillMode: Image.PreserveAspectFit } } ColumnLayout { spacing: 0 Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.fillWidth: true Layout.fillHeight: true TextMetrics { id: titleFontInfo font { bold: albumLabel.font.bold pointSize: albumLabel.font.pointSize } text: albumLabel.text } LabelWithToolTip { id: mainLabel text: title Layout.fillWidth: true Layout.alignment: Qt.AlignLeft elide: Text.ElideRight color: myPalette.highlightedText font.pointSize: elisaTheme.defaultFontPointSize * 2.5 font.bold: true Layout.bottomMargin: titleFontInfo.height * 0.5 } LabelWithToolTip { id: authorLabel text: artist Layout.fillWidth: true Layout.alignment: Qt.AlignLeft elide: Text.ElideRight color: myPalette.highlightedText font.pointSize: elisaTheme.defaultFontPointSize * 1.5 layer.effect: Glow { cached: true color: myPalette.shadow radius: 4.0 samples: 9 } } LabelWithToolTip { id: albumLabel text: album Layout.fillWidth: true Layout.alignment: Qt.AlignLeft elide: Text.ElideRight color: myPalette.highlightedText font.weight: Font.Light font.pointSize: elisaTheme.defaultFontPointSize * 1 layer.effect: Glow { cached: true color: myPalette.shadow radius: 4.0 samples: 9 } } RatingStar { id: mainRating visible: ratingVisible starSize: elisaTheme.ratingStarSize starRating: trackRating Layout.alignment: Qt.AlignLeft } } LabelWithToolTip { id: remainingTracksLabel text: i18np("1 track remaining", "%1 tracks remaining", tracksCount) Layout.alignment: Qt.AlignRight | Qt.AlignBottom Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin * 1.75 elide: Text.ElideRight visible: tracksCount > 0 color: myPalette.highlightedText } } Item { Layout.fillHeight: true Layout.fillWidth: true } } 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] 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/MediaPlayerControl.qml b/src/qml/MediaPlayerControl.qml index f6d3b7ca..0af1445e 100644 --- a/src/qml/MediaPlayerControl.qml +++ b/src/qml/MediaPlayerControl.qml @@ -1,572 +1,620 @@ /* * Copyright 2016 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import QtQuick.Controls 2.2 import org.kde.elisa 1.0 import QtQuick.Controls 1.4 as Controls1 FocusScope { property double volume property int position property int duration property bool muted property bool isPlaying property bool seekable property bool playEnabled property bool skipForwardEnabled property bool skipBackwardEnabled + property bool isMaximized property bool shuffle property bool repeat signal play() signal pause() signal playPrevious() signal playNext() signal seek(int position) + signal maximize() + signal minimize() + id: musicWidget SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } Rectangle { anchors.fill: parent color: myPalette.midlight opacity: elisaTheme.mediaPlayerControlOpacity } RowLayout { anchors.fill: parent spacing: 5 + Button { + Layout.preferredWidth: elisaTheme.smallControlButtonSize + Layout.preferredHeight: elisaTheme.smallControlButtonSize + Layout.alignment: Qt.AlignVCenter + Layout.maximumWidth: elisaTheme.smallControlButtonSize + Layout.maximumHeight: elisaTheme.smallControlButtonSize + Layout.minimumWidth: elisaTheme.smallControlButtonSize + Layout.minimumHeight: elisaTheme.smallControlButtonSize + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : 0 + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : 0 + + contentItem: Image { + anchors.fill: parent + source: Qt.resolvedUrl(musicWidget.isMaximized ? elisaTheme.minimizeIcon : elisaTheme.maximizeIcon) + + width: elisaTheme.smallControlButtonSize + height: elisaTheme.smallControlButtonSize + + sourceSize.width: elisaTheme.smallControlButtonSize + sourceSize.height: elisaTheme.smallControlButtonSize + + fillMode: Image.PreserveAspectFit + opacity: 1.0 + } + + background: Rectangle { + border.width: 0 + opacity: 0.0 + } + + onClicked: { + musicWidget.isMaximized = !musicWidget.isMaximized + } + } + RoundButton { focus: skipBackwardEnabled Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignVCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : 0 Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : 0 enabled: skipBackwardEnabled hoverEnabled: true onClicked: { musicWidget.playPrevious() } contentItem: Image { anchors.fill: parent source: Qt.resolvedUrl(LayoutMirroring.enabled ? elisaTheme.skipForwardIcon : elisaTheme.skipBackwardIcon) width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit opacity: skipBackwardEnabled ? 1.0 : 0.6 } background: Rectangle { color: "transparent" border.color: (parent.hovered || parent.activeFocus) ? myPalette.highlight : "transparent" border.width: 1 radius: elisaTheme.smallControlButtonSize Behavior on border.color { ColorAnimation { duration: 300 } } } } RoundButton { focus: playEnabled Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignVCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize enabled: playEnabled hoverEnabled: true onClicked: { if (musicWidget.isPlaying) { musicWidget.pause() } else { musicWidget.play() } } contentItem: Image { anchors.fill: parent source: { if (musicWidget.isPlaying) Qt.resolvedUrl(elisaTheme.pauseIcon) else Qt.resolvedUrl(elisaTheme.playIcon) } width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit mirror: LayoutMirroring.enabled opacity: playEnabled ? 1.0 : 0.6 } background: Rectangle { color: "transparent" border.color: (parent.hovered || parent.activeFocus) ? myPalette.highlight : "transparent" border.width: 1 radius: elisaTheme.smallControlButtonSize Behavior on border.color { ColorAnimation { duration: 300 } } } } RoundButton { focus: skipForwardEnabled Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignVCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize enabled: skipForwardEnabled hoverEnabled: true onClicked: { musicWidget.playNext() } contentItem: Image { anchors.fill: parent source: Qt.resolvedUrl(LayoutMirroring.enabled ? elisaTheme.skipBackwardIcon : elisaTheme.skipForwardIcon) width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit opacity: skipForwardEnabled ? 1.0 : 0.6 } background: Rectangle { color: "transparent" border.color: (parent.hovered || parent.activeFocus) ? myPalette.highlight : "transparent" border.width: 1 radius: elisaTheme.smallControlButtonSize Behavior on border.color { ColorAnimation { duration: 300 } } } } TextMetrics { id: durationTextMetrics text: i18nc("This is used to preserve a fixed width for the duration text.", "00:00:00") } LabelWithToolTip { id: positionLabel text: timeIndicator.progressDuration color: myPalette.text Layout.alignment: Qt.AlignVCenter Layout.fillHeight: true Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : elisaTheme.layoutHorizontalMargin * 2 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : elisaTheme.layoutHorizontalMargin * 2 Layout.preferredWidth: durationTextMetrics.width+5 // be in the safe side verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignRight ProgressIndicator { id: timeIndicator position: musicWidget.position } } Slider { property bool seekStarted: false property int seekValue id: musicProgress from: 0 to: musicWidget.duration Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 enabled: musicWidget.seekable && musicWidget.playEnabled live: true onValueChanged: { if (seekStarted) { seekValue = value } } onPressedChanged: { if (pressed) { seekStarted = true; seekValue = value } else { musicWidget.seek(seekValue) seekStarted = false; } } background: Rectangle { x: musicProgress.leftPadding y: musicProgress.topPadding + musicProgress.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 6 width: musicProgress.availableWidth height: implicitHeight radius: 3 color: myPalette.dark Rectangle { x: (LayoutMirroring.enabled ? musicProgress.visualPosition * parent.width : 0) width: LayoutMirroring.enabled ? parent.width - musicProgress.visualPosition * parent.width: musicProgress.handle.x + radius height: parent.height color: myPalette.text radius: 3 } } handle: Rectangle { x: musicProgress.leftPadding + musicProgress.visualPosition * (musicProgress.availableWidth - width) y: musicProgress.topPadding + musicProgress.availableHeight / 2 - height / 2 implicitWidth: 18 implicitHeight: 18 radius: 9 color: myPalette.base border.width: 1 border.color: musicProgress.pressed ? myPalette.text : myPalette.dark } } LabelWithToolTip { id: durationLabel text: durationIndicator.progressDuration color: myPalette.text Layout.alignment: Qt.AlignVCenter Layout.fillHeight: true Layout.rightMargin: !LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 Layout.leftMargin: LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 Layout.preferredWidth: durationTextMetrics.width verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft ProgressIndicator { id: durationIndicator position: musicWidget.duration } } Image { id: volumeIcon source: if (musicWidget.muted) Qt.resolvedUrl(elisaTheme.playerVolumeMutedIcon) else Qt.resolvedUrl(elisaTheme.playerVolumeIcon) Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignVCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit visible: false } RoundButton { focus: true Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignVCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize hoverEnabled: true onClicked: musicWidget.muted = !musicWidget.muted contentItem: Image { anchors.fill: parent source: if (musicWidget.muted) Qt.resolvedUrl(elisaTheme.playerVolumeMutedIcon) else Qt.resolvedUrl(elisaTheme.playerVolumeIcon) width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit } background: Rectangle { color: "transparent" border.color: (parent.hovered || parent.activeFocus) ? myPalette.highlight : "transparent" border.width: 1 radius: elisaTheme.smallControlButtonSize Behavior on border.color { ColorAnimation { duration: 300 } } } } Slider { id: volumeSlider from: 0 to: 100 value: musicWidget.volume onValueChanged: musicWidget.volume = value enabled: !muted Layout.alignment: Qt.AlignVCenter Layout.preferredWidth: elisaTheme.volumeSliderWidth Layout.maximumWidth: elisaTheme.volumeSliderWidth Layout.minimumWidth: elisaTheme.volumeSliderWidth Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin * 3 : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin * 3 : 0 width: elisaTheme.volumeSliderWidth background: Rectangle { x: volumeSlider.leftPadding y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 6 width: volumeSlider.availableWidth height: implicitHeight radius: 3 color: myPalette.dark opacity: muted ? 0.5 : 1 Rectangle { x: (LayoutMirroring.enabled ? volumeSlider.visualPosition * parent.width : 0) width: (LayoutMirroring.enabled ? parent.width - volumeSlider.visualPosition * parent.width : volumeSlider.visualPosition * parent.width) height: parent.height color: myPalette.text radius: 3 opacity: muted ? 0.5 : 1 } } handle: Rectangle { x: volumeSlider.leftPadding + volumeSlider.visualPosition * (volumeSlider.availableWidth - width) y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2 implicitWidth: 18 implicitHeight: 18 radius: 9 color: myPalette.base border.width: 1 border.color: volumeSlider.pressed ? myPalette.text : (muted ? myPalette.mid : myPalette.dark) } } RoundButton { focus: true id: shuffleButton Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize hoverEnabled: true onClicked: musicWidget.shuffle = !musicWidget.shuffle contentItem: Image { anchors.fill: parent source: musicWidget.shuffle ? Qt.resolvedUrl(elisaTheme.shuffleIcon) : Qt.resolvedUrl(elisaTheme.noShuffleIcon) width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit } background: Rectangle { color: "transparent" } } RoundButton { focus: true id: repeatButton Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize hoverEnabled: true onClicked: musicWidget.repeat = !musicWidget.repeat contentItem: Image { anchors.fill: parent source: musicWidget.repeat ? Qt.resolvedUrl(elisaTheme.repeatIcon) : Qt.resolvedUrl(elisaTheme.noRepeatIcon) width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit } background: Rectangle { color: "transparent" } } Controls1.ToolButton { id: menuButton action: applicationMenuAction Layout.preferredWidth: elisaTheme.smallControlButtonSize Layout.preferredHeight: elisaTheme.smallControlButtonSize Layout.alignment: Qt.AlignVCenter Layout.maximumWidth: elisaTheme.smallControlButtonSize Layout.maximumHeight: elisaTheme.smallControlButtonSize Layout.minimumWidth: elisaTheme.smallControlButtonSize Layout.minimumHeight: elisaTheme.smallControlButtonSize Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : elisaTheme.mediaPlayerHorizontalMargin * 1.5 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : elisaTheme.mediaPlayerHorizontalMargin * 1.5 } } onPositionChanged: { if (!musicProgress.seekStarted) { musicProgress.value = position } } onVolumeChanged: { console.log('volume of player controls changed: ' + volume) } + + onIsMaximizedChanged: + { + if (musicWidget.isMaximized) { + musicWidget.maximize() + } else { + musicWidget.minimize() + } + } } diff --git a/src/qml/Theme.qml b/src/qml/Theme.qml index 407ada72..7e2e2372 100644 --- a/src/qml/Theme.qml +++ b/src/qml/Theme.qml @@ -1,98 +1,100 @@ /* * Copyright 2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 Item { function dp(pixel) { // 96 - common, "base" DPI value return Math.round(pixel * logicalDpi / 96); } property string defaultAlbumImage: 'image://icon/media-optical-audio' property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string artistIcon: 'image://icon/view-media-artist' property string albumIcon: 'image://icon/view-media-album-cover' property string playlistIcon: 'image://icon/view-media-playlist' property string tracksIcon: 'image://icon/view-media-track' property string genresIcon: 'image://icon/view-media-genre' property string clearIcon: 'image://icon/edit-clear' property string skipBackwardIcon: 'image://icon/media-skip-backward' property string pauseIcon: 'image://icon/media-playback-pause' property string playIcon: 'image://icon/media-playback-start' property string skipForwardIcon: 'image://icon/media-skip-forward' property string pausedIndicatorIcon: 'image://icon/media-playback-paused' property string playingIndicatorIcon: 'image://icon/media-playback-playing' property string playerVolumeMutedIcon: 'image://icon/player-volume-muted' property string playerVolumeIcon: 'image://icon/player-volume' property string ratingIcon: 'image://icon/rating' property string ratingUnratedIcon: 'image://icon/rating-unrated' property string errorIcon: 'image://icon/error' property string repeatIcon: 'image://icon/media-repeat-all' property string shuffleIcon: 'image://icon/media-playlist-shuffle' property string noRepeatIcon: 'image://icon/media-repeat-none' property string noShuffleIcon: 'image://icon/media-playlist-normal' property string folderIcon: 'image://icon/document-open-folder' + property string maximizeIcon: 'image://icon/draw-arrow-down' + property string minimizeIcon: 'image://icon/draw-arrow-up' property int layoutHorizontalMargin: dp(8) property int layoutVerticalMargin: dp(6) property int delegateHeight: dp(28) property int delegateWithHeaderHeight: dp(86) property int trackDelegateHeight: dp(45) property int coverImageSize: dp(180) property int smallImageSize: dp(32) property int maximumMetadataWidth: dp(300) property int tooltipRadius: dp(3) property int shadowOffset: dp(2) property int delegateToolButtonSize: dp(34) property int smallDelegateToolButtonSize: dp(20) property int ratingStarSize: dp(15) property int mediaPlayerControlHeight: dp(42) property int mediaPlayerHorizontalMargin: dp(10) property real mediaPlayerControlOpacity: 0.6 property int smallControlButtonSize: dp(22) property int volumeSliderWidth: dp(100) property int dragDropPlaceholderHeight: dp(28) property int navigationBarHeight: dp(100) property int navigationBarFilterHeight: dp(44) property int gridDelegateHeight: dp(100) + layoutVerticalMargin + fontSize.height * 2 property int gridDelegateWidth: dp(100) property int viewSelectorDelegateHeight: dp(24) property int filterClearButtonMargin: layoutVerticalMargin property alias defaultFontPointSize: fontSize.font.pointSize Label { id: fontSize } } diff --git a/src/windows/WindowsTheme.qml b/src/windows/WindowsTheme.qml index efc29965..e079ebb8 100644 --- a/src/windows/WindowsTheme.qml +++ b/src/windows/WindowsTheme.qml @@ -1,98 +1,100 @@ /* * Copyright 2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 Item { function dp(pixel) { // 96 - common, "base" DPI value return Math.round(pixel * logicalDpi / 96); } property string defaultAlbumImage: 'image://icon/media-optical-audio' property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string artistIcon: 'image://icon/view-media-artist' property string albumIcon: 'image://icon/view-media-album-cover' property string playlistIcon: 'image://icon/view-media-playlist' property string tracksIcon: 'image://icon/view-media-track' property string genresIcon: 'image://icon/view-media-genre' property string clearIcon: 'image://icon/edit-clear' property string skipBackwardIcon: 'image://icon/media-skip-backward' property string pauseIcon: 'image://icon/media-playback-pause' property string playIcon: 'image://icon/media-playback-start' property string skipForwardIcon: 'image://icon/media-skip-forward' property string pausedIndicatorIcon: 'image://icon/media-playback-paused' property string playingIndicatorIcon: 'image://icon/media-playback-playing' property string playerVolumeMutedIcon: 'image://icon/player-volume-muted' property string playerVolumeIcon: 'image://icon/player-volume' property string ratingIcon: 'image://icon/rating' property string ratingUnratedIcon: 'image://icon/rating-unrated' property string errorIcon: 'image://icon/error' property string repeatIcon: 'image://icon/media-repeat-all' property string shuffleIcon: 'image://icon/media-playlist-shuffle' property string noRepeatIcon: 'image://icon/media-repeat-none' property string noShuffleIcon: 'image://icon/media-playlist-normal' property string folderIcon: 'image://icon/document-open-folder' + property string maximizeIcon: 'image://icon/draw-arrow-down' + property string minimizeIcon: 'image://icon/draw-arrow-up' property int layoutHorizontalMargin: dp(8) property int layoutVerticalMargin: dp(6) property int delegateHeight: dp(28) property int delegateWithHeaderHeight: dp(86) property int trackDelegateHeight: dp(45) property int coverImageSize: dp(180) property int smallImageSize: dp(32) property int maximumMetadataWidth: dp(300) property int tooltipRadius: dp(3) property int shadowOffset: dp(2) property int delegateToolButtonSize: dp(34) property int smallDelegateToolButtonSize: dp(20) property int ratingStarSize: dp(15) property int mediaPlayerControlHeight: dp(42) property int mediaPlayerHorizontalMargin: dp(10) property real mediaPlayerControlOpacity: 0.8 property int smallControlButtonSize: dp(22) property int volumeSliderWidth: dp(100) property int dragDropPlaceholderHeight: dp(28) property int navigationBarHeight: dp(100) property int navigationBarFilterHeight: dp(44) property int gridDelegateHeight: dp(100) + layoutVerticalMargin + fontSize.height * 2 property int gridDelegateWidth: dp(100) property int viewSelectorDelegateHeight: dp(24) property int filterClearButtonMargin: layoutVerticalMargin property alias defaultFontPointSize: fontSize.font.pointSize Label { id: fontSize } }