diff --git a/src/qml/ApplicationMenu.qml b/src/qml/ApplicationMenu.qml index b7a888ad..580fc5db 100644 --- a/src/qml/ApplicationMenu.qml +++ b/src/qml/ApplicationMenu.qml @@ -1,125 +1,114 @@ /* * Copyright 2016-2018 Matthieu Gallien * Copyright 2018 Alexander Stippich * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.3 import org.kde.elisa 1.0 Menu { id: applicationMenu title: i18nc("open application menu", "Application Menu") property var helpAction: elisa.action("help_contents") property var quitApplication: elisa.action("file_quit") property var reportBugAction: elisa.action("help_report_bug") property var aboutAppAction: elisa.action("help_about_app") property var configureShortcutsAction: elisa.action("options_configure_keybinding") property var configureAction: elisa.action("options_configure") - property var togglePlaylistAction: elisa.action("toggle_playlist") Action { text: i18nc("Refresh Music Collection application menu entry", "Refresh Music Collection") icon.name: "view-refresh" onTriggered: { applicationMenu.close() elisa.musicManager.resetMusicData() } } MenuSeparator { } Action { text: configureAction.text shortcut: configureAction.shortcut icon.name: elisa.iconName(configureAction.icon) onTriggered: { applicationMenu.close() configureAction.trigger() } } Action { text: configureShortcutsAction.text shortcut: configureShortcutsAction.shortcut icon.name: elisa.iconName(configureShortcutsAction.icon) onTriggered: { applicationMenu.close() configureShortcutsAction.trigger() } } - Action { - shortcut: togglePlaylistAction.shortcut - text: contentView.showPlaylist ? i18nc("Hide playlist", "Hide Playlist") : i18nc("Show playlist", "Show Playlist") - icon.name: "view-media-playlist" - onTriggered: { - contentView.showPlaylist = !contentView.showPlaylist - applicationMenu.close() - } - } - MenuSeparator { visible: reportBugAction.text !== "" } Action { text: reportBugAction.text shortcut: reportBugAction.shortcut icon.name: elisa.iconName(reportBugAction.icon) onTriggered: { applicationMenu.close() reportBugAction.trigger() } } MenuSeparator { visible: helpAction.text !== "" } Action { text: helpAction.text shortcut: helpAction.shortcut icon.name: elisa.iconName(helpAction.icon) onTriggered: { applicationMenu.close() helpAction.trigger() } } Action { text: aboutAppAction.text shortcut: aboutAppAction.shortcut icon.name: elisa.iconName(aboutAppAction.icon) onTriggered: { applicationMenu.close() aboutAppAction.trigger() } } MenuSeparator { visible: quitApplication.text !== "" } Action { text: quitApplication.text shortcut: quitApplication.shortcut icon.name: elisa.iconName(quitApplication.icon) onTriggered: quitApplication.trigger() } } diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index 9c14bd61..155a4734 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,350 +1,350 @@ /* * 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 import org.kde.kirigami 2.8 as Kirigami RowLayout { id: contentViewContainer spacing: 0 property bool showPlaylist property bool showExpandedFilterView property alias currentViewIndex: listViews.currentIndex function goBack() { viewManager.goBack() } function openArtist(name) { viewManager.openChildView(name, '', elisaTheme.artistIcon, 0, ElisaUtils.Artist, ViewManager.NoDiscHeaders) } function openAlbum(album, artist, image, albumID, showDiscHeader) { image = !image ? elisaTheme.defaultAlbumImage : image; viewManager.openChildView(album, artist, image, albumID, ElisaUtils.Album, showDiscHeader); } function openNowPlaying() { viewManager.closeAllViews(); } ViewManager { id: viewManager onSwitchOffAllViews: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > 1) { browseStackView.pop() } } onOpenGridView: { if (expectedDepth === 1) { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) } while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(dataGridView, { viewType: viewType, filterType: filterType, 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, }) } onOpenListView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(dataListView, { viewType: viewType, filterType: filterType, isSubPage: expectedDepth > 1, mainTitle: mainTitle, secondaryTitle: secondaryTitle, databaseId: databaseId, image: imageUrl, modelType: dataType, sortRole: sortRole, sortAscending: sortOrder, stackView: browseStackView, displaySingleAlbum: displaySingleAlbum, showSection: showDiscHeaders, opacity: 0, radioCase: radioCase }) } onSwitchFilesBrowserView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(filesBrowserView, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, opacity: 0, }) } onSwitchContextView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(albumContext, { 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, "")) } Kirigami.Separator { id: viewSelectorSeparatorItem Layout.fillHeight: true } FocusScope { id: mainContentView focus: true Layout.fillHeight: true Layout.fillWidth: true MouseArea { anchors.fill: parent acceptedButtons: Qt.BackButton onClicked: goBack() } Rectangle { 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 } } } } } Kirigami.Separator { id: playListSeparatorItem Layout.fillHeight: true } MediaPlayListView { id: playList Layout.fillHeight: true onStartPlayback: elisa.audioControl.ensurePlay() onPausePlayback: elisa.audioControl.playPause() } states: [ State { name: "browsingViewsNoPlaylist" - when: contentViewContainer.showPlaylist === false + when: contentViewContainer.showPlaylist === false || mainWindow.width < elisaTheme.viewSelectorSmallSizeThreshold PropertyChanges { target: playList Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } PropertyChanges { target: playListSeparatorItem visible: false } }, State { name: 'browsingViews' - when: contentViewContainer.showPlaylist === true + when: contentViewContainer.showPlaylist === true || mainWindow.width >= elisaTheme.viewSelectorSmallSizeThreshold PropertyChanges { target: playList Layout.minimumWidth: contentViewContainer.width * 0.28 Layout.maximumWidth: contentViewContainer.width * 0.28 Layout.preferredWidth: contentViewContainer.width * 0.28 } PropertyChanges { target: playListSeparatorItem visible: true } } ] transitions: Transition { NumberAnimation { properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" easing.type: Easing.InOutQuad duration: 300 } } Component { id: dataGridView DataGridView { StackView.onActivated: viewManager.viewIsLoaded(viewType) expandedFilterView: showExpandedFilterView } } Component { id: dataListView DataListView { StackView.onActivated: viewManager.viewIsLoaded(viewType) expandedFilterView: showExpandedFilterView } } Component { id: filesBrowserView FileBrowserView { StackView.onActivated: viewManager.viewIsLoaded(viewType) expandedFilterView: showExpandedFilterView } } Component { id: albumContext ContextView { StackView.onActivated: viewManager.viewIsLoaded(viewType) databaseId: elisa.manageHeaderBar.databaseId trackType: elisa.manageHeaderBar.trackType title: elisa.manageHeaderBar.title artistName: elisa.manageHeaderBar.artist albumName: elisa.manageHeaderBar.album albumArtUrl: elisa.manageHeaderBar.image fileUrl: elisa.manageHeaderBar.fileUrl } } } diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml index 1319a4e6..4e076ef6 100644 --- a/src/qml/ElisaMainWindow.qml +++ b/src/qml/ElisaMainWindow.qml @@ -1,333 +1,333 @@ /* * 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.3 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 + minimumWidth: 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") Accessible.role: Accessible.Application Accessible.name: title property var goBackAction: elisa.action("go_back") property var seekAction: elisa.action("Seek") property var scrubAction: elisa.action("Scrub") property var playPauseAction: elisa.action("Play-Pause") property var findAction: elisa.action("edit_find") Action { shortcut: goBackAction.shortcut onTriggered: contentView.goBack() } Action { shortcut: seekAction.shortcut onTriggered: elisa.audioControl.seek(headerBar.playerControl.position + 10000) } Action { shortcut: scrubAction.shortcut onTriggered: elisa.audioControl.seek(headerBar.playerControl.position - 10000) } Action { shortcut: playPauseAction.shortcut onTriggered: elisa.audioControl.playPause() } Action { shortcut: findAction.shortcut onTriggered: persistentSettings.expandedFilterView = !persistentSettings.expandedFilterView } 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 expandedFilterView: false property bool showPlaylist: true property bool headerBarIsMaximized: false } Connections { target: headerBar.playerControl onOpenMenu: { if (applicationMenu.visible) { applicationMenu.close() } else { applicationMenu.popup(mainWindow.width - applicationMenu.width, headerBar.height) } } } 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.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 } Rectangle { color: myPalette.base anchors.fill: parent ColumnLayout { anchors.fill: parent spacing: 0 HeaderBar { id: headerBar focus: true Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.fillWidth: true tracksCount: elisa.mediaPlayList.remainingTracks album: (elisa.manageHeaderBar.album !== undefined ? elisa.manageHeaderBar.album : '') title: elisa.manageHeaderBar.title artist: (elisa.manageHeaderBar.artist !== undefined ? elisa.manageHeaderBar.artist : '') albumArtist: (elisa.manageHeaderBar.albumArtist !== undefined ? elisa.manageHeaderBar.albumArtist : '') image: elisa.manageHeaderBar.image albumID: elisa.manageHeaderBar.albumId 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: elisa.mediaPlayList.repeatPlay playerControl.shuffle: elisa.mediaPlayList.randomPlay 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 onOpenArtist: { contentView.openArtist(artist) } onOpenNowPlaying: { contentView.openNowPlaying() } onOpenAlbum: { contentView.openAlbum(album, albumArtist, image, albumID) } TrackImportNotification { id: importedTracksCountNotification anchors { right: headerBar.right top: headerBar.top rightMargin: elisaTheme.layoutHorizontalMargin * 1.75 topMargin: elisaTheme.layoutHorizontalMargin * 3 } } Binding { id: indexerBusyBinding target: importedTracksCountNotification property: 'indexingRunning' value: elisa.musicManager.indexerBusy when: elisa.musicManager !== undefined } Binding { target: importedTracksCountNotification property: 'importedTracksCount' value: elisa.musicManager.importedTracksCount when: elisa.musicManager !== undefined } } Rectangle { Layout.fillWidth: true height: 1 color: myPalette.mid } ContentView { id: contentView Layout.fillHeight: true Layout.fillWidth: true showPlaylist: persistentSettings.showPlaylist showExpandedFilterView: persistentSettings.expandedFilterView } } } StateGroup { id: mainWindowState states: [ State { name: "headerBarIsNormal" when: !headerBar.isMaximized changes: [ PropertyChanges { target: mainWindow minimumHeight: 600 explicit: true }, PropertyChanges { target: headerBar Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight } ] }, State { name: "headerBarIsMaximized" when: headerBar.isMaximized changes: [ PropertyChanges { target: mainWindow minimumHeight: 120 + elisaTheme.mediaPlayerControlHeight explicit: true }, PropertyChanges { target: headerBar Layout.minimumHeight: mainWindow.height Layout.maximumHeight: mainWindow.height } ] } ] transitions: Transition { NumberAnimation { properties: "Layout.minimumHeight, Layout.maximumHeight, minimumHeight" easing.type: Easing.InOutQuad duration: 300 } } } Component.onCompleted: { elisa.initialize() if (persistentSettings.playListState) { elisa.mediaPlayList.persistentState = persistentSettings.playListState } if (persistentSettings.audioPlayerState) { elisa.audioControl.persistentState = persistentSettings.audioPlayerState } elisa.mediaPlayList.randomPlay = Qt.binding(function() { return headerBar.playerControl.shuffle }) elisa.mediaPlayList.repeatPlay = Qt.binding(function() { return headerBar.playerControl.repeat }) 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/MediaPlayerControl.qml b/src/qml/MediaPlayerControl.qml index 6720a913..deff6920 100644 --- a/src/qml/MediaPlayerControl.qml +++ b/src/qml/MediaPlayerControl.qml @@ -1,456 +1,476 @@ /* * 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 QtQuick.Controls 2.3 import org.kde.elisa 1.0 FocusScope { property alias volume: volumeSlider.value 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 openMenu() signal maximize() signal minimize() id: musicWidget SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } Action { id: applicationMenuAction text: i18nc("open application menu", "Application Menu") icon.name: "application-menu" onTriggered: openMenu() } Action { id: repeatAction text: i18nc("toggle repeat mode for playlist", "Toggle Repeat") icon.name: musicWidget.repeat ? "media-repeat-all" : "media-repeat-none" onTriggered: musicWidget.repeat = !musicWidget.repeat } Action { id: shuffleAction text: i18nc("toggle shuffle mode for playlist", "Toggle Shuffle") icon.name: musicWidget.shuffle ? "media-playlist-shuffle" : "media-playlist-normal" onTriggered: musicWidget.shuffle = !musicWidget.shuffle } Action { id: muteAction text: i18nc("toggle mute mode for player", "Toggle Mute") icon.name: musicWidget.muted ? "player-volume-muted" : "player-volume" onTriggered: musicWidget.muted = !musicWidget.muted } Action { id: playPauseAction text: i18nc("toggle play and pause for the audio player", "Toggle Play and Pause") icon.name: musicWidget.isPlaying? "media-playback-pause" : "media-playback-start" onTriggered: musicWidget.isPlaying ? musicWidget.pause() : musicWidget.play() enabled: playEnabled } Action { id: skipBackwardAction text: i18nc("skip backward in playlists", "Skip Backward") icon.name: musicWidget.LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward" onTriggered: musicWidget.playPrevious() enabled: skipBackwardEnabled } Action { id: skipForwardAction text: i18nc("skip forward in playlists", "Skip Forward") icon.name: musicWidget.LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward" onTriggered: musicWidget.playNext() enabled: skipForwardEnabled } Action { id: minimizeMaximizeAction text: i18nc("toggle between maximized and minimized ivre", "Toggle Maximize") icon.name: musicWidget.isMaximized ? "draw-arrow-up" : "draw-arrow-down" onTriggered: musicWidget.isMaximized = !musicWidget.isMaximized } Rectangle { anchors.fill: parent color: myPalette.midlight opacity: elisaTheme.mediaPlayerControlOpacity } RowLayout { anchors.fill: parent spacing: 0 FlatButtonWithToolTip { id: minimizeMaximizeButton action: minimizeMaximizeAction icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : 0 Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.mediaPlayerHorizontalMargin : 0 } FlatButtonWithToolTip { id: skipBackwardButton action: skipBackwardAction focus: skipBackwardEnabled icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize } FlatButtonWithToolTip { id: playPauseButton action: playPauseAction focus: playEnabled icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize } FlatButtonWithToolTip { id: skipForwardButton action: skipForwardAction focus: skipForwardEnabled icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize } 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.boundingRect.width - durationTextMetrics.boundingRect.x) + 5 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignRight ProgressIndicator { id: timeIndicator position: musicWidget.position } } MouseArea { id: seekWheelHandler Layout.alignment: Qt.AlignVCenter Layout.fillHeight: true Layout.fillWidth: true Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 acceptedButtons: Qt.NoButton onWheel: { if (wheel.angleDelta.y > 0) { musicWidget.seek(position + 10000) } else { musicWidget.seek(position - 10000) } } // Synthesized slider background that's not actually a part of the // slider. This is done so the slider's own background can be full // height yet transparent, for easier clicking Rectangle { x: musicProgress.leftPadding y: musicProgress.topPadding + musicProgress.availableHeight / 2 - height / 2 implicitWidth: seekWheelHandler.width implicitHeight: 6 color: myPalette.dark radius: 3 } Slider { property bool seekStarted: false property int seekValue id: musicProgress from: 0 to: musicWidget.duration width: parent.width anchors.centerIn: parent 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; } } // This only provides a full-height area for clicking; see // https://bugs.kde.org/show_bug.cgi?id=408703. The actual visual // background is generated above ^^ background: Rectangle { x: musicProgress.leftPadding y: musicProgress.topPadding + musicProgress.availableHeight / 2 - height / 2 anchors.fill: parent implicitWidth: seekWheelHandler.width implicitHeight: seekWheelHandler.height color: "transparent" Rectangle { anchors.verticalCenter: parent.verticalCenter x: (LayoutMirroring.enabled ? musicProgress.visualPosition * parent.width : 0) width: LayoutMirroring.enabled ? parent.width - musicProgress.visualPosition * parent.width: musicProgress.handle.x + radius height: 6 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.boundingRect.width - durationTextMetrics.boundingRect.x) verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft ProgressIndicator { id: durationIndicator position: musicWidget.duration } } FlatButtonWithToolTip { id: muteButton action: muteAction focus: true icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize } MouseArea { id: audioWheelHandler Layout.preferredWidth: elisaTheme.volumeSliderWidth Layout.maximumWidth: elisaTheme.volumeSliderWidth Layout.minimumWidth: elisaTheme.volumeSliderWidth Layout.fillHeight: true acceptedButtons: Qt.NoButton onWheel: { if (wheel.angleDelta.y > 0) { volumeSlider.increase() } else { volumeSlider.decrease() } } // Synthesized slider background that's not actually a part of the // slider. This is done so the slider's own background can be full // height yet transparent, for easier clicking Rectangle { x: volumeSlider.leftPadding y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2 implicitWidth: audioWheelHandler.width implicitHeight: 6 radius: 3 color: myPalette.dark opacity: muted ? 0.5 : 1 } Slider { id: volumeSlider from: 0 to: 100 stepSize: 5 enabled: !muted anchors.centerIn: parent width: elisaTheme.volumeSliderWidth // This only provides a full-height area for clicking; see // https://bugs.kde.org/show_bug.cgi?id=408703. The actual visual // background is generated above ^^ background: Rectangle { anchors.fill: parent implicitWidth: audioWheelHandler.width implicitHeight: audioWheelHandler.height color: "transparent" Rectangle { anchors.verticalCenter: parent.verticalCenter x: (LayoutMirroring.enabled ? volumeSlider.visualPosition * parent.width : 0) width: (LayoutMirroring.enabled ? parent.width - volumeSlider.visualPosition * parent.width : volumeSlider.visualPosition * parent.width) height: 6 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) } } } FlatButtonWithToolTip { focus: true action: shuffleAction id: shuffleButton icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize } FlatButtonWithToolTip { focus: true action: repeatAction id: repeatButton icon.width: elisaTheme.smallControlButtonSize icon.height: elisaTheme.smallControlButtonSize } + // Not a FlatButtonWithToolTip because we want text + Button { + id: showHidePlaylistAction + action: Action { + shortcut: elisa.action("toggle_playlist").shortcut + onTriggered: contentView.showPlaylist = !contentView.showPlaylist + } + + visible: mainWindow.width >= elisaTheme.viewSelectorSmallSizeThreshold + + flat: true + text: i18n("Show Playlist") + icon.name: "view-media-playlist" + + checkable: true + checked: contentView.showPlaylist + + activeFocusOnTab: true + Keys.onReturnPressed: action.trigger() + Accessible.onPressAction: action.trigger() + + } + FlatButtonWithToolTip { id: menuButton action: applicationMenuAction focus: true icon.width: elisaTheme.smallControlButtonSize icon.height: 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 } } onIsMaximizedChanged: { if (musicWidget.isMaximized) { musicWidget.maximize() } else { musicWidget.minimize() } } Component.onCompleted: { var elementList = [menuButton, repeatButton, shuffleButton, muteButton, skipForwardButton, skipBackwardButton, playPauseButton, minimizeMaximizeButton] for (var i=0; i * Copyright 2018 Alexander Stippich * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 //explore menu from Qt 5.10 once we can require it, but it is item-based import Qt.labs.platform 1.0 import org.kde.elisa 1.0 Menu { id: applicationMenu title: i18nc("open application menu", "Application Menu") property var helpAction: elisa.action("help_contents") property var quitApplication: elisa.action("file_quit") property var reportBugAction: elisa.action("help_report_bug") property var aboutAppAction: elisa.action("help_about_app") property var configureShortcutsAction: elisa.action("options_configure_keybinding") property var configureAction: elisa.action("options_configure") property var togglePlaylistAction: elisa.action("toggle_playlist") MenuItem { text: i18nc("Refresh Music Collection application menu entry", "Refresh Music Collection") iconName: "collection-rescan-amarok" onTriggered: elisa.musicManager.resetMusicData() } MenuSeparator { } MenuItem { text: configureAction.text shortcut: configureAction.shortcut iconName: elisa.iconName(configureAction.icon) onTriggered: configureAction.trigger() visible: configureAction.text !== "" } MenuItem { text: configureShortcutsAction.text shortcut: configureShortcutsAction.shortcut iconName: elisa.iconName(configureShortcutsAction.icon) onTriggered: configureShortcutsAction.trigger() visible: configureShortcutsAction.text !== "" } MenuItem { shortcut: togglePlaylistAction.shortcut - text: contentView.showPlaylist ? i18nc("Hide playlist", "Hide Playlist") : i18nc("Show playlist", "Show Playlist") + text: i18n("Show Playlist") iconName: "view-media-playlist" + checkable: true + checked: contentView.showPlaylist onTriggered: contentView.showPlaylist = !contentView.showPlaylist enabled: contentView.currentViewIndex != 0 } MenuSeparator { visible: reportBugAction.text !== "" } MenuItem { text: reportBugAction.text shortcut: reportBugAction.shortcut iconName: elisa.iconName(reportBugAction.icon) onTriggered: reportBugAction.trigger() visible: reportBugAction.text !== "" } MenuSeparator { visible: helpAction.text !== "" } MenuItem { text: helpAction.text shortcut: helpAction.shortcut iconName: elisa.iconName(helpAction.icon) onTriggered: helpAction.trigger() visible: helpAction.text !== "" } MenuItem { text: aboutAppAction.text shortcut: aboutAppAction.shortcut iconName: elisa.iconName(aboutAppAction.icon) onTriggered: aboutAppAction.trigger() visible: aboutAppAction.text !== "" } MenuSeparator { visible: quitApplication.text !== "" } MenuItem { text: quitApplication.text shortcut: quitApplication.shortcut iconName: elisa.iconName(quitApplication.icon) onTriggered: quitApplication.trigger() visible: quitApplication.text !== "" } }