From e16c40d57c3c0a3adc512940928808fb79c9d7d3 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Sat, 30 Oct 2021 17:10:19 +0000 Subject: [PATCH] Add mobile bottom navbar, use Titles toolbar on mobile, and add blurred image background to mobile player --- src/qml/BottomToolbar.qml | 62 ++++++ src/qml/EpisodeListPage.qml | 11 +- src/qml/FeedListPage.qml | 14 +- src/qml/FooterBar.qml | 57 +++++- src/qml/MinimizedPlayerControls.qml | 43 ++-- src/qml/PlayerControls.qml | 304 +++++++++++++++++----------- src/qml/main.qml | 211 ++++++++++--------- src/resources.qrc | 1 + 8 files changed, 461 insertions(+), 242 deletions(-) create mode 100644 src/qml/BottomToolbar.qml diff --git a/src/qml/BottomToolbar.qml b/src/qml/BottomToolbar.qml new file mode 100644 index 00000000..80f54905 --- /dev/null +++ b/src/qml/BottomToolbar.qml @@ -0,0 +1,62 @@ +/* + * Copyright 2021 Devin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.19 as Kirigami + +import org.kde.kasts 1.0 + +Kirigami.NavigationTabBar { + id: root + + property alias toolbarHeight: root.implicitHeight + property bool transparentBackground: false + + shadow: false + + actions: [ + Kirigami.Action { + iconName: "view-media-playlist" + text: i18n("Queue") + checked: "QueuePage" === SettingsManager.lastOpenedPage + onTriggered: { + pushPage("QueuePage"); + SettingsManager.lastOpenedPage = "QueuePage"; // for persistency + } + }, + Kirigami.Action { + iconName: "bookmarks" + text: i18n("Subscriptions") + checked: "FeedListPage" === SettingsManager.lastOpenedPage + onTriggered: { + pushPage("FeedListPage"); + SettingsManager.lastOpenedPage = "FeedListPage"; // for persistency + } + }, + Kirigami.Action { + iconName: "rss" + text: i18n("Episodes") + checked: "EpisodeListPage" === SettingsManager.lastOpenedPage + onTriggered: { + pushPage("EpisodeListPage") + SettingsManager.lastOpenedPage = "EpisodeListPage" // for persistency + } + }, + Kirigami.Action { + iconName: "settings-configure" + text: i18n("Settings") + checked: "SettingsPage" === SettingsManager.lastOpenedPage + onTriggered: { + applicationWindow().pageStack.clear() + applicationWindow().pageStack.push("qrc:/SettingsPage.qml", {}, { + title: i18n("Settings") + }) + } + } + ] +} diff --git a/src/qml/EpisodeListPage.qml b/src/qml/EpisodeListPage.qml index 8573a7ea..b20822df 100644 --- a/src/qml/EpisodeListPage.qml +++ b/src/qml/EpisodeListPage.qml @@ -26,12 +26,21 @@ Kirigami.ScrollablePage { } actions.main: Kirigami.Action { + iconName: "download" + text: i18n("Downloads") + onTriggered: { + pushPage("DownloadListPage") + SettingsManager.lastOpenedPage = "DownloadListPage" // for persistency + } + } + + actions.left: Kirigami.Action { iconName: "view-filter" text: i18n("Filter") onTriggered: filterTypeOverlay.open(); } - actions.left: Kirigami.Action { + actions.right: Kirigami.Action { iconName: "view-refresh" text: i18n("Refresh All Podcasts") onTriggered: refreshing = true diff --git a/src/qml/FeedListPage.qml b/src/qml/FeedListPage.qml index 16699563..7b7d60b9 100644 --- a/src/qml/FeedListPage.qml +++ b/src/qml/FeedListPage.qml @@ -33,14 +33,22 @@ Kirigami.ScrollablePage { } actions.main: Kirigami.Action { - text: i18n("Add Podcast") - iconName: "list-add" + visible: Kirigami.Settings.isMobile + text: i18n("Discover") + iconName: "search" onTriggered: { - addSheet.open() + applicationWindow().pageStack.push("qrc:/DiscoverPage.qml"); } } contextualActions: [ + Kirigami.Action { + text: i18n("Add Podcast") + iconName: "list-add" + onTriggered: { + addSheet.open() + } + }, Kirigami.Action { text: i18n("Refresh All Podcasts") iconName: "view-refresh" diff --git a/src/qml/FooterBar.qml b/src/qml/FooterBar.qml index bfa04c47..80ec99af 100644 --- a/src/qml/FooterBar.qml +++ b/src/qml/FooterBar.qml @@ -8,6 +8,7 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 as Controls import QtQuick.Layouts 1.14 +import QtGraphicalEffects 1.0 import org.kde.kirigami 2.14 as Kirigami @@ -20,6 +21,8 @@ Flickable { property bool isMaximized: contentY === contentHeight / 2 + property int contentToPlayerSpacing: 0 + boundsBehavior: Flickable.StopAtBounds NumberAnimation on contentY { @@ -51,6 +54,10 @@ Flickable { onReleased: footerBar.resetToBoundsOnFlick() } + function close() { + toClose.restart(); + } + function resetToBoundsOnFlick() { if (!atYBeginning || !atYEnd) { if (footerBar.verticalVelocity > 0) { @@ -90,8 +97,11 @@ Flickable { // a cover for content underneath the panel Rectangle { id: coverUnderneath - color: Kirigami.Theme.backgroundColor anchors.fill: parent + + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + color: Kirigami.Theme.backgroundColor } } @@ -101,23 +111,52 @@ Flickable { anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right - height: root.height + root.miniplayerSize + height: root.height + root.miniplayerSize + contentToPlayerSpacing spacing: 0 - MinimizedPlayerControls { - id: playControlItem - + Controls.Control { + implicitHeight: root.miniplayerSize + contentToPlayerSpacing Layout.fillWidth: true - Layout.minimumHeight: root.miniplayerSize - Layout.alignment: Qt.AlignTop - focus: true + padding: 0 + + background: Image { + opacity: 0.2 + source: AudioManager.entry.cachedImage + asynchronous: true + + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + + layer.enabled: true + layer.effect: HueSaturation { + cached: true + + lightness: 0.7 + saturation: 0.9 + + layer.enabled: true + layer.effect: FastBlur { + cached: true + radius: 64 + transparentBorder: false + } + } + } + + MinimizedPlayerControls { + id: playControlItem + height: root.miniplayerSize + focus: true + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + } } PlayerControls { id: mobileTrackPlayer Layout.fillWidth: true Layout.fillHeight: true - Layout.margins: Kirigami.Units.largeSpacing * 2 } } } diff --git a/src/qml/MinimizedPlayerControls.qml b/src/qml/MinimizedPlayerControls.qml index 77fb0996..fb665c7e 100644 --- a/src/qml/MinimizedPlayerControls.qml +++ b/src/qml/MinimizedPlayerControls.qml @@ -1,5 +1,6 @@ /** * SPDX-FileCopyrightText: 2021 Bart De Vries + * SPDX-FileCopyrightText: 2021 Devin Lin * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ @@ -8,25 +9,20 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 as Controls import QtQuick.Layouts 1.14 import QtMultimedia 5.15 +import QtGraphicalEffects 1.0 import org.kde.kirigami 2.12 as Kirigami import org.kde.kasts 1.0 Item { - property int miniplayerheight: Kirigami.Units.gridUnit * 3 + property int miniplayerheight: Math.round(Kirigami.Units.gridUnit * 3) property int progressbarheight: Kirigami.Units.gridUnit / 6 - property int buttonsize: Kirigami.Units.gridUnit * 2 - height: miniplayerheight + progressbarheight + property int buttonsize: Kirigami.Units.gridUnit * 1.5 + height: miniplayerheight visible: AudioManager.entry - // Set background - Rectangle { - anchors.fill: parent - color: Kirigami.Theme.backgroundColor - } - // progress bar for limited width (phones) Rectangle { id: miniprogressbar @@ -41,44 +37,50 @@ Item { RowLayout { id: footerrowlayout - anchors.topMargin: miniprogressbar.height anchors.fill: parent - Item { + spacing: 0 + + Rectangle { Layout.fillHeight: true Layout.fillWidth: true + // press feedback + color: (trackClick.pressed || trackClick.containsMouse) ? Qt.rgba(0, 0, 0, 0.05) : "transparent" + RowLayout { anchors.fill: parent ImageWithFallback { imageSource: AudioManager.entry.cachedImage - Layout.preferredHeight: miniplayerheight - Layout.preferredWidth: miniplayerheight + Layout.fillHeight: true + Layout.preferredWidth: height } // track information ColumnLayout { - Layout.fillHeight: true + Layout.maximumHeight: parent.height Layout.fillWidth: true Layout.leftMargin: Kirigami.Units.smallSpacing + spacing: Kirigami.Units.smallSpacing + Controls.Label { id: mainLabel text: AudioManager.entry.title wrapMode: Text.Wrap - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.fillWidth: true horizontalAlignment: Text.AlignLeft elide: Text.ElideRight maximumLineCount: 1 - //font.weight: Font.Bold font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1 + font.weight: Font.Medium } Controls.Label { id: feedLabel text: AudioManager.entry.feed.name wrapMode: Text.Wrap - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.fillWidth: true horizontalAlignment: Text.AlignLeft elide: Text.ElideRight @@ -101,9 +103,10 @@ Item { icon.height: parent.parent.buttonsize icon.width: parent.parent.buttonsize flat: true - Layout.fillHeight: true - Layout.maximumHeight: parent.parent.miniplayerheight - Layout.maximumWidth: height + Layout.preferredHeight: parent.parent.miniplayerheight - Kirigami.Units.smallSpacing * 2 + Layout.preferredWidth: height + Layout.leftMargin: Kirigami.Units.smallSpacing + Layout.rightMargin: Kirigami.Units.smallSpacing onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play() Layout.alignment: Qt.AlignVCenter } diff --git a/src/qml/PlayerControls.qml b/src/qml/PlayerControls.qml index 9b8d38db..6be41805 100644 --- a/src/qml/PlayerControls.qml +++ b/src/qml/PlayerControls.qml @@ -1,5 +1,6 @@ /** * SPDX-FileCopyrightText: 2021 Bart De Vries + * SPDX-FileCopyrightText: 2021 Devin Lin * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ @@ -21,16 +22,47 @@ Kirigami.Page { clip: true Layout.margins: 0 - padding: 0 + leftPadding: 0 + rightPadding: 0 + topPadding: 0 bottomPadding: Kirigami.Units.gridUnit + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + + background: Image { + opacity: 0.2 + source: AudioManager.entry.cachedImage + asynchronous: true + + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + + layer.enabled: true + layer.effect: HueSaturation { + cached: true + + lightness: 0.7 + saturation: 0.9 + + layer.enabled: true + layer.effect: FastBlur { + cached: true + radius: 100 + transparentBorder: false + } + } + } + ColumnLayout { id: playerControlsColumn anchors.fill: parent - anchors.topMargin:0 + anchors.topMargin: Kirigami.Units.largeSpacing * 2 + anchors.bottomMargin: Kirigami.Units.largeSpacing * 2 + Controls.Button { id: swipeUpButton - property int swipeUpButtonSize: Kirigami.Units.gridUnit * 2 + property int swipeUpButtonSize: Kirigami.Units.iconSizes.smallMedium icon.name: "arrow-down" icon.height: swipeUpButtonSize icon.width: swipeUpButtonSize @@ -39,97 +71,118 @@ Kirigami.Page { Layout.topMargin: 0 onClicked: toClose.restart() } + Controls.SwipeView { id: swipeView currentIndex: 0 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.preferredWidth: parent.width - Layout.preferredHeight: parent.height - media.height - indicator.height - swipeUpButton.height + Layout.preferredHeight: parent.height - media.height - indicator.height - indicator.Layout.bottomMargin - swipeUpButton.height Layout.margins: 0 - Item { - property int textMargin: Kirigami.Units.gridUnit // margin above and below the text below the image - ImageWithFallback { - id: coverImage - imageSource: AudioManager.entry ? AudioManager.entry.cachedImage : "no-image" - imageFillMode: Image.PreserveAspectCrop - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: Math.max(0, parent.height - (height + imageLabels.height + 2*parent.textMargin))/2 - height: Math.min(Math.min(parent.height, Kirigami.Units.iconSizes.enormous * 3) - (imageLabels.height + 2 * parent.textMargin), - Math.min(parent.width, Kirigami.Units.iconSizes.enormous * 3)) - width: height - fractionalRadius: 1 / 20 - } - ColumnLayout { - id: imageLabels - anchors.top: coverImage.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: parent.textMargin - Controls.Label { - text: AudioManager.entry ? AudioManager.entry.title : i18n("No Title") - elide: Text.ElideRight - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: parent.width + + Controls.Control { + leftPadding: Kirigami.Units.largeSpacing * 2 + rightPadding: Kirigami.Units.largeSpacing * 2 + + contentItem: Item { + property int textMargin: Kirigami.Units.gridUnit // margin above and below the text below the image + ImageWithFallback { + id: coverImage + imageSource: AudioManager.entry ? AudioManager.entry.cachedImage : "no-image" + imageFillMode: Image.PreserveAspectCrop + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: Math.max(0, parent.height - (height + imageLabels.height + 2*parent.textMargin))/2 + height: Math.min(Math.min(parent.height, Kirigami.Units.iconSizes.enormous * 3) - (imageLabels.height + 2 * parent.textMargin), + Math.min(parent.width, Kirigami.Units.iconSizes.enormous * 3)) + width: height + fractionalRadius: 1 / 20 } - Controls.Label { - text: AudioManager.entry ? AudioManager.entry.feed.name : i18n("No Podcast Title") - elide: Text.ElideRight - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: parent.width - opacity: 0.6 - } - } - } - Item { - Flickable { - anchors.fill: parent - clip: true - contentHeight: description.height ColumnLayout { - id: description - width: parent.width - Kirigami.Heading { - text: AudioManager.entry ? AudioManager.entry.title : i18n("No Track Title") - level: 3 - wrapMode: Text.WordWrap - Layout.fillWidth: true - Layout.bottomMargin: Kirigami.Units.largeSpacing + id: imageLabels + anchors.top: coverImage.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: parent.textMargin + Controls.Label { + text: AudioManager.entry ? AudioManager.entry.title : i18n("No Title") + elide: Text.ElideRight + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: parent.width + font.weight: Font.Medium } Controls.Label { - id: text - text: AudioManager.entry ? AudioManager.entry.content : i18n("No Track Loaded") - verticalAlignment: Text.AlignTop - baseUrl: AudioManager.entry ? AudioManager.entry.baseUrl : "" - textFormat: Text.RichText - wrapMode: Text.WordWrap - onLinkActivated: Qt.openUrlExternally(link) - Layout.fillWidth: true + text: AudioManager.entry ? AudioManager.entry.feed.name : i18n("No Podcast Title") + elide: Text.ElideRight + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: parent.width + opacity: 0.6 } } } } - Item { - Kirigami.PlaceholderMessage { - visible: chapterList.count === 0 - width: parent.width - anchors.centerIn: parent + Controls.Control { + leftPadding: Kirigami.Units.largeSpacing * 2 + rightPadding: Kirigami.Units.largeSpacing * 2 - text: i18n("No chapter marks found.") - } - ListView { - id: chapterList - model: ChapterModel { - enclosureId: AudioManager.entry.id - enclosurePath: AudioManager.entry.enclosure.path + contentItem: Item { + Flickable { + anchors.fill: parent + anchors.leftMargin: playerControlsColumn.anchors.margins + clip: true + contentHeight: description.height + ColumnLayout { + id: description + width: parent.width + Kirigami.Heading { + text: AudioManager.entry ? AudioManager.entry.title : i18n("No Track Title") + level: 3 + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.bottomMargin: Kirigami.Units.largeSpacing + } + Controls.Label { + id: text + text: AudioManager.entry ? AudioManager.entry.content : i18n("No Track Loaded") + verticalAlignment: Text.AlignTop + baseUrl: AudioManager.entry ? AudioManager.entry.baseUrl : "" + textFormat: Text.RichText + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + Layout.fillWidth: true + } + } } - clip: true - visible: chapterList.count !== 0 - anchors.fill: parent - delegate: ChapterListDelegate { - entry: AudioManager.entry + } + } + + Controls.Control { + leftPadding: Kirigami.Units.largeSpacing * 2 + rightPadding: Kirigami.Units.largeSpacing * 2 + + contentItem: Item { + Kirigami.PlaceholderMessage { + visible: chapterList.count === 0 + + width: parent.width + anchors.centerIn: parent + + text: i18n("No chapters found.") + } + ListView { + id: chapterList + model: ChapterModel { + enclosureId: AudioManager.entry.id + enclosurePath: AudioManager.entry.enclosure.path + } + clip: true + visible: chapterList.count !== 0 + anchors.fill: parent + delegate: ChapterListDelegate { + entry: AudioManager.entry + } } } } @@ -141,19 +194,20 @@ Kirigami.Page { count: swipeView.count currentIndex: swipeView.currentIndex Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: Kirigami.Units.gridUnit } + Item { id: media implicitHeight: mediaControls.height + Layout.leftMargin: Kirigami.Units.largeSpacing * 2 + Layout.rightMargin: Kirigami.Units.largeSpacing * 2 Layout.fillWidth: true - Layout.margins: 0 ColumnLayout { id: mediaControls - //implicitHeight: controls.height - anchors.left: parent.left anchors.right: parent.right anchors.margins: 0 @@ -161,7 +215,8 @@ Kirigami.Page { Controls.Slider { enabled: AudioManager.entry Layout.fillWidth: true - Layout.margins: 0 + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing padding: 0 from: 0 to: AudioManager.duration @@ -170,16 +225,20 @@ Kirigami.Page { } RowLayout { id: controls - Layout.margins: 0 + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing Layout.fillWidth: true Controls.Label { + Layout.alignment: Qt.AlignLeft padding: Kirigami.Units.largeSpacing text: AudioManager.formattedPosition + font: Kirigami.Theme.smallFont } Item { Layout.fillWidth: true } Item { + Layout.alignment: Qt.AlignRight Layout.preferredHeight: endLabel.implicitHeight Layout.preferredWidth: endLabel.implicitWidth Controls.Label { @@ -190,6 +249,7 @@ Kirigami.Page { text: (SettingsManager.toggleRemainingTime) ? "-" + AudioManager.formattedLeftDuration : AudioManager.formattedDuration + font: Kirigami.Theme.smallFont } MouseArea { @@ -199,7 +259,11 @@ Kirigami.Page { } } } + RowLayout { + id: bottomRow + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing Layout.maximumWidth: Number.POSITIVE_INFINITY //TODO ? Layout.fillWidth: true Layout.margins: 0 @@ -207,8 +271,9 @@ Kirigami.Page { // Make button width scale properly on narrow windows instead of overflowing property int buttonSize: Math.min(playButton.implicitWidth, ((playerControlsColumn.width - 4 * spacing) / 5 - playButton.leftPadding - playButton.rightPadding)) - property int iconSize: Kirigami.Units.gridUnit * 2 + property int iconSize: Kirigami.Units.gridUnit * 1.5 + // left section Controls.Button { // Use contentItem and a Label because using plain "text" // does not rescale automatically if the text changes @@ -221,49 +286,56 @@ Kirigami.Page { playbackRateDialog.open() } flat: true - Layout.alignment: Qt.AlignHCenter padding: 0 implicitWidth: playButton.width implicitHeight: playButton.height } - Controls.Button { - icon.name: "media-seek-backward" - icon.height: parent.iconSize - icon.width: parent.iconSize - flat: true + + // middle section + RowLayout { + spacing: Kirigami.Units.largeSpacing Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: parent.buttonSize - onClicked: AudioManager.skipBackward() - enabled: AudioManager.canSkipBackward - } - Controls.Button { - id: playButton - icon.name: AudioManager.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start" - icon.height: parent.iconSize - icon.width: parent.iconSize - flat: true - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: parent.buttonSize - onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play() - enabled: AudioManager.canPlay - } - Controls.Button { - icon.name: "media-seek-forward" - icon.height: parent.iconSize - icon.width: parent.iconSize - flat: true - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: parent.buttonSize - onClicked: AudioManager.skipForward() - enabled: AudioManager.canSkipForward + Controls.Button { + icon.name: "media-seek-backward" + icon.height: bottomRow.iconSize + icon.width: bottomRow.iconSize + flat: true + Layout.alignment: Qt.AlignRight + Layout.preferredWidth: bottomRow.buttonSize + onClicked: AudioManager.skipBackward() + enabled: AudioManager.canSkipBackward + } + Controls.Button { + id: playButton + icon.name: AudioManager.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start" + icon.height: bottomRow.iconSize + icon.width: bottomRow.iconSize + flat: true + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: bottomRow.buttonSize + onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play() + enabled: AudioManager.canPlay + } + Controls.Button { + icon.name: "media-seek-forward" + icon.height: bottomRow.iconSize + icon.width: bottomRow.iconSize + flat: true + Layout.alignment: Qt.AlignLeft + Layout.preferredWidth: bottomRow.buttonSize + onClicked: AudioManager.skipForward() + enabled: AudioManager.canSkipForward + } } + + // right section Controls.Button { icon.name: "media-skip-forward" - icon.height: parent.iconSize - icon.width: parent.iconSize + icon.height: bottomRow.iconSize + icon.width: bottomRow.iconSize flat: true - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: parent.buttonSize + Layout.alignment: Qt.AlignRight + Layout.preferredWidth: bottomRow.buttonSize onClicked: AudioManager.next() enabled: AudioManager.canGoNext } diff --git a/src/qml/main.qml b/src/qml/main.qml index 1ce7c23f..e08d441c 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -8,6 +8,7 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 as Controls import QtQuick.Layouts 1.14 +import QtGraphicalEffects 1.12 import org.kde.kirigami 2.14 as Kirigami import org.kde.kasts.solidextras 1.0 @@ -21,18 +22,33 @@ Kirigami.ApplicationWindow { minimumWidth: Kirigami.Units.gridUnit * 17 minimumHeight: Kirigami.Units.gridUnit * 20 - property var miniplayerSize: Kirigami.Units.gridUnit * 3 + Kirigami.Units.gridUnit / 6 - property int bottomMessageSpacing: Kirigami.Settings.isMobile ? Kirigami.Units.largeSpacing * 9 + ( AudioManager.entry ? ( footerLoader.item.contentY == 0 ? miniplayerSize : 0 ) : 0 ) : Kirigami.Units.largeSpacing * 2 + property var miniplayerSize: Math.round(Kirigami.Units.gridUnit * 3) + Kirigami.Units.gridUnit / 6 + property int bottomMessageSpacing: { + if (Kirigami.Settings.isMobile) { + return Kirigami.Units.largeSpacing + ( AudioManager.entry ? ( footerLoader.item.contentY == 0 ? miniplayerSize : 0 ) : 0 ) + (root.footer.height); + } else { + return Kirigami.Units.largeSpacing * 2; + } + } property int originalWidth: Kirigami.Units.gridUnit * 10 property var lastFeed: "" property string currentPage: "" property bool isWidescreen: root.width >= root.height onIsWidescreenChanged: { - if (!Kirigami.Settings.isMobile) { - changeNavigation(!isWidescreen); + changeNavigation(!isWidescreen); + } + + function changeNavigation(isNarrow) { + if (isNarrow) { + globalDrawer.collapsed = true + globalDrawer.width = Layout.implicitWidth + } else { + globalDrawer.collapsed = false + globalDrawer.width = originalWidth } } + function getPage(page) { switch (page) { case "QueuePage": return "qrc:/QueuePage.qml"; @@ -55,8 +71,10 @@ Kirigami.ApplicationWindow { currentPage = SettingsManager.lastOpenedPage pageStack.initialPage = getPage(SettingsManager.lastOpenedPage) - // move mobile handles to toolbar - pageStack.globalToolBar.canContainHandles = true; + if (Kirigami.Settings.isMobile) { + pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.ToolBar; + pageStack.globalToolBar.showNavigationButtons = Kirigami.ApplicationHeaderStyle.ShowBackButton; + } // Delete played enclosures if set in settings if (SettingsManager.autoDeleteOnPlayed == 2) { @@ -73,93 +91,78 @@ Kirigami.ApplicationWindow { } } - globalDrawer: Kirigami.GlobalDrawer { - isMenu: false - modal: Kirigami.Settings.isMobile - collapsible: !Kirigami.Settings.isMobile - header: Kirigami.AbstractApplicationHeader { - visible: !Kirigami.Settings.isMobile - } + globalDrawer: sidebar.item + Loader { + id: sidebar + active: !Kirigami.Settings.isMobile || root.isWidescreen + sourceComponent: Kirigami.GlobalDrawer { + width: 200 + modal: false + isMenu: false + collapsible: !Kirigami.Settings.isMobile + header: Kirigami.AbstractApplicationHeader {} - Component.onCompleted: { - if (!Kirigami.Settings.isMobile) { - Kirigami.Theme.colorSet = Kirigami.Theme.Window; - Kirigami.Theme.inherit = false; - } - } + Kirigami.Theme.colorSet: Kirigami.Theme.Window + Kirigami.Theme.inherit: false - // make room at the bottom for miniplayer - handle.anchors.bottomMargin: (( AudioManager.entry && Kirigami.Settings.isMobile ) ? (footerLoader.item.contentY == 0 ? miniplayerSize : 0) : 0) + Kirigami.Units.smallSpacing - handleVisible: Kirigami.Settings.isMobile ? !AudioManager.entry || footerLoader.item.contentY === 0 : false - showHeaderWhenCollapsed: true - actions: [ - Kirigami.Action { - text: i18n("Queue") - iconName: "source-playlist" - checked: currentPage == "QueuePage" - onTriggered: { - pushPage("QueuePage") - SettingsManager.lastOpenedPage = "QueuePage" // for persistency + actions: [ + Kirigami.Action { + text: i18n("Queue") + iconName: "source-playlist" + checked: currentPage == "QueuePage" + onTriggered: { + pushPage("QueuePage") + SettingsManager.lastOpenedPage = "QueuePage" // for persistency + } + }, + Kirigami.Action { + text: i18n("Discover") + iconName: "search" + checked: currentPage == "DiscoverPage" + onTriggered: { + pushPage("DiscoverPage") + SettingsManager.lastOpenedPage = "DiscoverPage" // for persistency + } + }, + Kirigami.Action { + text: i18n("Episodes") + iconName: "rss" + checked: currentPage == "EpisodeListPage" + onTriggered: { + pushPage("EpisodeListPage") + SettingsManager.lastOpenedPage = "EpisodeListPage" // for persistency + } + }, + Kirigami.Action { + text: i18n("Subscriptions") + iconName: "bookmarks" + checked: currentPage == "FeedListPage" + onTriggered: { + pushPage("FeedListPage") + SettingsManager.lastOpenedPage = "FeedListPage" // for persistency + } + }, + Kirigami.Action { + text: i18n("Downloads") + iconName: "download" + checked: currentPage == "DownloadListPage" + onTriggered: { + pushPage("DownloadListPage") + SettingsManager.lastOpenedPage = "DownloadListPage" // for persistency + } + }, + Kirigami.Action { + text: i18n("Settings") + iconName: "settings-configure" + checked: currentPage == "SettingsPage" + onTriggered: { + root.pageStack.layers.clear() + root.pageStack.pushDialogLayer("qrc:/SettingsPage.qml", {}, { + title: i18n("Settings") + }) + } } - }, - Kirigami.Action { - text: i18n("Discover") - iconName: "search" - checked: currentPage == "DiscoverPage" - onTriggered: { - pushPage("DiscoverPage") - SettingsManager.lastOpenedPage = "DiscoverPage" // for persistency - } - }, - Kirigami.Action { - text: i18n("Episodes") - iconName: "rss" - checked: currentPage == "EpisodeListPage" - onTriggered: { - pushPage("EpisodeListPage") - SettingsManager.lastOpenedPage = "EpisodeListPage" // for persistency - } - }, - Kirigami.Action { - text: i18n("Subscriptions") - iconName: "bookmarks" - checked: currentPage == "FeedListPage" - onTriggered: { - pushPage("FeedListPage") - SettingsManager.lastOpenedPage = "FeedListPage" // for persistency - } - }, - Kirigami.Action { - text: i18n("Downloads") - iconName: "download" - checked: currentPage == "DownloadListPage" - onTriggered: { - pushPage("DownloadListPage") - SettingsManager.lastOpenedPage = "DownloadListPage" // for persistency - } - }, - Kirigami.Action { - text: i18n("Settings") - iconName: "settings-configure" - checked: currentPage == "SettingsPage" - onTriggered: { - root.pageStack.layers.clear() - root.pageStack.pushDialogLayer("qrc:/SettingsPage.qml", {}, { - title: i18n("Settings") - }) - } - } - ] - } - - function changeNavigation(isNarrow) { - if(isNarrow) { - globalDrawer.collapsed = true - globalDrawer.width = Layout.implicitWidth - } - else { - globalDrawer.collapsed = false - globalDrawer.width = originalWidth + ] } } @@ -190,9 +193,7 @@ Kirigami.ApplicationWindow { active: !Kirigami.Settings.isMobile visible: active - sourceComponent: HeaderBar { - focus: true - } + sourceComponent: HeaderBar { focus: true } } // create space at the bottom to show miniplayer without it hiding stuff @@ -209,8 +210,32 @@ Kirigami.ApplicationWindow { sourceComponent: FooterBar { contentHeight: root.height * 2 focus: true + contentToPlayerSpacing: footer.active ? footer.item.height + 1 : 0 } + } + Loader { + id: footerShadowLoader + active: footer.active && !footerLoader.active + anchors.fill: footer + + sourceComponent: RectangularGlow { + glowRadius: 5 + spread: 0.3 + color: Qt.rgba(0.0, 0.0, 0.0, 0.1) + } + } + + footer: Loader { + visible: active + active: Kirigami.Settings.isMobile && !root.isWidescreen + sourceComponent: BottomToolbar { + transparentBackground: footerLoader.active + opacity: (!footerLoader.item || footerLoader.item.contentY === 0) ? 1 : 0 + Behavior on opacity { + NumberAnimation { duration: Kirigami.Units.shortDuration } + } + } } // Notification that shows the progress of feed updates diff --git a/src/resources.qrc b/src/resources.qrc index 42449ebf..bbc26ea6 100755 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -34,6 +34,7 @@ qml/Settings/NetworkSettingsPage.qml qml/Settings/StorageSettingsPage.qml qml/Settings/SynchronizationSettingsPage.qml + qml/BottomToolbar.qml qtquickcontrols2.conf ../kasts.svg