Add mobile bottom navbar, use Titles toolbar on mobile, and add blurred image background to mobile player

This commit is contained in:
Devin Lin 2021-10-30 17:10:19 +00:00
parent f861f4e802
commit e16c40d57c
8 changed files with 461 additions and 242 deletions

62
src/qml/BottomToolbar.qml Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright 2021 Devin Lin <espidev@gmail.com>
*
* 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")
})
}
}
]
}

View File

@ -26,12 +26,21 @@ Kirigami.ScrollablePage {
} }
actions.main: Kirigami.Action { actions.main: Kirigami.Action {
iconName: "download"
text: i18n("Downloads")
onTriggered: {
pushPage("DownloadListPage")
SettingsManager.lastOpenedPage = "DownloadListPage" // for persistency
}
}
actions.left: Kirigami.Action {
iconName: "view-filter" iconName: "view-filter"
text: i18n("Filter") text: i18n("Filter")
onTriggered: filterTypeOverlay.open(); onTriggered: filterTypeOverlay.open();
} }
actions.left: Kirigami.Action { actions.right: Kirigami.Action {
iconName: "view-refresh" iconName: "view-refresh"
text: i18n("Refresh All Podcasts") text: i18n("Refresh All Podcasts")
onTriggered: refreshing = true onTriggered: refreshing = true

View File

@ -33,14 +33,22 @@ Kirigami.ScrollablePage {
} }
actions.main: Kirigami.Action { actions.main: Kirigami.Action {
text: i18n("Add Podcast") visible: Kirigami.Settings.isMobile
iconName: "list-add" text: i18n("Discover")
iconName: "search"
onTriggered: { onTriggered: {
addSheet.open() applicationWindow().pageStack.push("qrc:/DiscoverPage.qml");
} }
} }
contextualActions: [ contextualActions: [
Kirigami.Action {
text: i18n("Add Podcast")
iconName: "list-add"
onTriggered: {
addSheet.open()
}
},
Kirigami.Action { Kirigami.Action {
text: i18n("Refresh All Podcasts") text: i18n("Refresh All Podcasts")
iconName: "view-refresh" iconName: "view-refresh"

View File

@ -8,6 +8,7 @@
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14 as Controls import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.0
import org.kde.kirigami 2.14 as Kirigami import org.kde.kirigami 2.14 as Kirigami
@ -20,6 +21,8 @@ Flickable {
property bool isMaximized: contentY === contentHeight / 2 property bool isMaximized: contentY === contentHeight / 2
property int contentToPlayerSpacing: 0
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
NumberAnimation on contentY { NumberAnimation on contentY {
@ -51,6 +54,10 @@ Flickable {
onReleased: footerBar.resetToBoundsOnFlick() onReleased: footerBar.resetToBoundsOnFlick()
} }
function close() {
toClose.restart();
}
function resetToBoundsOnFlick() { function resetToBoundsOnFlick() {
if (!atYBeginning || !atYEnd) { if (!atYBeginning || !atYEnd) {
if (footerBar.verticalVelocity > 0) { if (footerBar.verticalVelocity > 0) {
@ -90,8 +97,11 @@ Flickable {
// a cover for content underneath the panel // a cover for content underneath the panel
Rectangle { Rectangle {
id: coverUnderneath id: coverUnderneath
color: Kirigami.Theme.backgroundColor
anchors.fill: parent 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.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: root.height + root.miniplayerSize height: root.height + root.miniplayerSize + contentToPlayerSpacing
spacing: 0 spacing: 0
MinimizedPlayerControls { Controls.Control {
id: playControlItem implicitHeight: root.miniplayerSize + contentToPlayerSpacing
Layout.fillWidth: true Layout.fillWidth: true
Layout.minimumHeight: root.miniplayerSize padding: 0
Layout.alignment: Qt.AlignTop
focus: true 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 { PlayerControls {
id: mobileTrackPlayer id: mobileTrackPlayer
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
Layout.margins: Kirigami.Units.largeSpacing * 2
} }
} }
} }

View File

@ -1,5 +1,6 @@
/** /**
* SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be> * SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be>
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
* *
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL * 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.Controls 2.14 as Controls
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import QtMultimedia 5.15 import QtMultimedia 5.15
import QtGraphicalEffects 1.0
import org.kde.kirigami 2.12 as Kirigami import org.kde.kirigami 2.12 as Kirigami
import org.kde.kasts 1.0 import org.kde.kasts 1.0
Item { 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 progressbarheight: Kirigami.Units.gridUnit / 6
property int buttonsize: Kirigami.Units.gridUnit * 2 property int buttonsize: Kirigami.Units.gridUnit * 1.5
height: miniplayerheight + progressbarheight height: miniplayerheight
visible: AudioManager.entry visible: AudioManager.entry
// Set background
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.backgroundColor
}
// progress bar for limited width (phones) // progress bar for limited width (phones)
Rectangle { Rectangle {
id: miniprogressbar id: miniprogressbar
@ -41,44 +37,50 @@ Item {
RowLayout { RowLayout {
id: footerrowlayout id: footerrowlayout
anchors.topMargin: miniprogressbar.height
anchors.fill: parent anchors.fill: parent
Item { spacing: 0
Rectangle {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
// press feedback
color: (trackClick.pressed || trackClick.containsMouse) ? Qt.rgba(0, 0, 0, 0.05) : "transparent"
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
ImageWithFallback { ImageWithFallback {
imageSource: AudioManager.entry.cachedImage imageSource: AudioManager.entry.cachedImage
Layout.preferredHeight: miniplayerheight Layout.fillHeight: true
Layout.preferredWidth: miniplayerheight Layout.preferredWidth: height
} }
// track information // track information
ColumnLayout { ColumnLayout {
Layout.fillHeight: true Layout.maximumHeight: parent.height
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing Layout.leftMargin: Kirigami.Units.smallSpacing
spacing: Kirigami.Units.smallSpacing
Controls.Label { Controls.Label {
id: mainLabel id: mainLabel
text: AudioManager.entry.title text: AudioManager.entry.title
wrapMode: Text.Wrap wrapMode: Text.Wrap
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 1 maximumLineCount: 1
//font.weight: Font.Bold
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1 font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1
font.weight: Font.Medium
} }
Controls.Label { Controls.Label {
id: feedLabel id: feedLabel
text: AudioManager.entry.feed.name text: AudioManager.entry.feed.name
wrapMode: Text.Wrap wrapMode: Text.Wrap
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight elide: Text.ElideRight
@ -101,9 +103,10 @@ Item {
icon.height: parent.parent.buttonsize icon.height: parent.parent.buttonsize
icon.width: parent.parent.buttonsize icon.width: parent.parent.buttonsize
flat: true flat: true
Layout.fillHeight: true Layout.preferredHeight: parent.parent.miniplayerheight - Kirigami.Units.smallSpacing * 2
Layout.maximumHeight: parent.parent.miniplayerheight Layout.preferredWidth: height
Layout.maximumWidth: height Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play() onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play()
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
} }

View File

@ -1,5 +1,6 @@
/** /**
* SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be> * SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be>
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
* *
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL * 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 clip: true
Layout.margins: 0 Layout.margins: 0
padding: 0 leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: Kirigami.Units.gridUnit 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 { ColumnLayout {
id: playerControlsColumn id: playerControlsColumn
anchors.fill: parent anchors.fill: parent
anchors.topMargin:0 anchors.topMargin: Kirigami.Units.largeSpacing * 2
anchors.bottomMargin: Kirigami.Units.largeSpacing * 2
Controls.Button { Controls.Button {
id: swipeUpButton id: swipeUpButton
property int swipeUpButtonSize: Kirigami.Units.gridUnit * 2 property int swipeUpButtonSize: Kirigami.Units.iconSizes.smallMedium
icon.name: "arrow-down" icon.name: "arrow-down"
icon.height: swipeUpButtonSize icon.height: swipeUpButtonSize
icon.width: swipeUpButtonSize icon.width: swipeUpButtonSize
@ -39,97 +71,118 @@ Kirigami.Page {
Layout.topMargin: 0 Layout.topMargin: 0
onClicked: toClose.restart() onClicked: toClose.restart()
} }
Controls.SwipeView { Controls.SwipeView {
id: swipeView id: swipeView
currentIndex: 0 currentIndex: 0
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.preferredWidth: parent.width 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 Layout.margins: 0
Item {
property int textMargin: Kirigami.Units.gridUnit // margin above and below the text below the image Controls.Control {
ImageWithFallback { leftPadding: Kirigami.Units.largeSpacing * 2
id: coverImage rightPadding: Kirigami.Units.largeSpacing * 2
imageSource: AudioManager.entry ? AudioManager.entry.cachedImage : "no-image"
imageFillMode: Image.PreserveAspectCrop contentItem: Item {
anchors.horizontalCenter: parent.horizontalCenter property int textMargin: Kirigami.Units.gridUnit // margin above and below the text below the image
anchors.top: parent.top ImageWithFallback {
anchors.topMargin: Math.max(0, parent.height - (height + imageLabels.height + 2*parent.textMargin))/2 id: coverImage
height: Math.min(Math.min(parent.height, Kirigami.Units.iconSizes.enormous * 3) - (imageLabels.height + 2 * parent.textMargin), imageSource: AudioManager.entry ? AudioManager.entry.cachedImage : "no-image"
Math.min(parent.width, Kirigami.Units.iconSizes.enormous * 3)) imageFillMode: Image.PreserveAspectCrop
width: height anchors.horizontalCenter: parent.horizontalCenter
fractionalRadius: 1 / 20 anchors.top: parent.top
} anchors.topMargin: Math.max(0, parent.height - (height + imageLabels.height + 2*parent.textMargin))/2
ColumnLayout { height: Math.min(Math.min(parent.height, Kirigami.Units.iconSizes.enormous * 3) - (imageLabels.height + 2 * parent.textMargin),
id: imageLabels Math.min(parent.width, Kirigami.Units.iconSizes.enormous * 3))
anchors.top: coverImage.bottom width: height
anchors.left: parent.left fractionalRadius: 1 / 20
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.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 { ColumnLayout {
id: description id: imageLabels
width: parent.width anchors.top: coverImage.bottom
Kirigami.Heading { anchors.left: parent.left
text: AudioManager.entry ? AudioManager.entry.title : i18n("No Track Title") anchors.right: parent.right
level: 3 anchors.topMargin: parent.textMargin
wrapMode: Text.WordWrap Controls.Label {
Layout.fillWidth: true text: AudioManager.entry ? AudioManager.entry.title : i18n("No Title")
Layout.bottomMargin: Kirigami.Units.largeSpacing elide: Text.ElideRight
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: parent.width
font.weight: Font.Medium
} }
Controls.Label { Controls.Label {
id: text text: AudioManager.entry ? AudioManager.entry.feed.name : i18n("No Podcast Title")
text: AudioManager.entry ? AudioManager.entry.content : i18n("No Track Loaded") elide: Text.ElideRight
verticalAlignment: Text.AlignTop Layout.alignment: Qt.AlignHCenter
baseUrl: AudioManager.entry ? AudioManager.entry.baseUrl : "" Layout.maximumWidth: parent.width
textFormat: Text.RichText opacity: 0.6
wrapMode: Text.WordWrap
onLinkActivated: Qt.openUrlExternally(link)
Layout.fillWidth: true
} }
} }
} }
} }
Item {
Kirigami.PlaceholderMessage {
visible: chapterList.count === 0
width: parent.width Controls.Control {
anchors.centerIn: parent leftPadding: Kirigami.Units.largeSpacing * 2
rightPadding: Kirigami.Units.largeSpacing * 2
text: i18n("No chapter marks found.") contentItem: Item {
} Flickable {
ListView { anchors.fill: parent
id: chapterList anchors.leftMargin: playerControlsColumn.anchors.margins
model: ChapterModel { clip: true
enclosureId: AudioManager.entry.id contentHeight: description.height
enclosurePath: AudioManager.entry.enclosure.path 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 { Controls.Control {
entry: AudioManager.entry 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 count: swipeView.count
currentIndex: swipeView.currentIndex currentIndex: swipeView.currentIndex
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Kirigami.Units.gridUnit
} }
Item { Item {
id: media id: media
implicitHeight: mediaControls.height implicitHeight: mediaControls.height
Layout.leftMargin: Kirigami.Units.largeSpacing * 2
Layout.rightMargin: Kirigami.Units.largeSpacing * 2
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 0
ColumnLayout { ColumnLayout {
id: mediaControls id: mediaControls
//implicitHeight: controls.height
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.margins: 0 anchors.margins: 0
@ -161,7 +215,8 @@ Kirigami.Page {
Controls.Slider { Controls.Slider {
enabled: AudioManager.entry enabled: AudioManager.entry
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 0 Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
padding: 0 padding: 0
from: 0 from: 0
to: AudioManager.duration to: AudioManager.duration
@ -170,16 +225,20 @@ Kirigami.Page {
} }
RowLayout { RowLayout {
id: controls id: controls
Layout.margins: 0 Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
Controls.Label { Controls.Label {
Layout.alignment: Qt.AlignLeft
padding: Kirigami.Units.largeSpacing padding: Kirigami.Units.largeSpacing
text: AudioManager.formattedPosition text: AudioManager.formattedPosition
font: Kirigami.Theme.smallFont
} }
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
} }
Item { Item {
Layout.alignment: Qt.AlignRight
Layout.preferredHeight: endLabel.implicitHeight Layout.preferredHeight: endLabel.implicitHeight
Layout.preferredWidth: endLabel.implicitWidth Layout.preferredWidth: endLabel.implicitWidth
Controls.Label { Controls.Label {
@ -190,6 +249,7 @@ Kirigami.Page {
text: (SettingsManager.toggleRemainingTime) ? text: (SettingsManager.toggleRemainingTime) ?
"-" + AudioManager.formattedLeftDuration "-" + AudioManager.formattedLeftDuration
: AudioManager.formattedDuration : AudioManager.formattedDuration
font: Kirigami.Theme.smallFont
} }
MouseArea { MouseArea {
@ -199,7 +259,11 @@ Kirigami.Page {
} }
} }
} }
RowLayout { RowLayout {
id: bottomRow
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.maximumWidth: Number.POSITIVE_INFINITY //TODO ? Layout.maximumWidth: Number.POSITIVE_INFINITY //TODO ?
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 0 Layout.margins: 0
@ -207,8 +271,9 @@ Kirigami.Page {
// Make button width scale properly on narrow windows instead of overflowing // 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 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 { Controls.Button {
// Use contentItem and a Label because using plain "text" // Use contentItem and a Label because using plain "text"
// does not rescale automatically if the text changes // does not rescale automatically if the text changes
@ -221,49 +286,56 @@ Kirigami.Page {
playbackRateDialog.open() playbackRateDialog.open()
} }
flat: true flat: true
Layout.alignment: Qt.AlignHCenter
padding: 0 padding: 0
implicitWidth: playButton.width implicitWidth: playButton.width
implicitHeight: playButton.height implicitHeight: playButton.height
} }
Controls.Button {
icon.name: "media-seek-backward" // middle section
icon.height: parent.iconSize RowLayout {
icon.width: parent.iconSize spacing: Kirigami.Units.largeSpacing
flat: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: parent.buttonSize Controls.Button {
onClicked: AudioManager.skipBackward() icon.name: "media-seek-backward"
enabled: AudioManager.canSkipBackward icon.height: bottomRow.iconSize
} icon.width: bottomRow.iconSize
Controls.Button { flat: true
id: playButton Layout.alignment: Qt.AlignRight
icon.name: AudioManager.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start" Layout.preferredWidth: bottomRow.buttonSize
icon.height: parent.iconSize onClicked: AudioManager.skipBackward()
icon.width: parent.iconSize enabled: AudioManager.canSkipBackward
flat: true }
Layout.alignment: Qt.AlignHCenter Controls.Button {
Layout.preferredWidth: parent.buttonSize id: playButton
onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play() icon.name: AudioManager.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
enabled: AudioManager.canPlay icon.height: bottomRow.iconSize
} icon.width: bottomRow.iconSize
Controls.Button { flat: true
icon.name: "media-seek-forward" Layout.alignment: Qt.AlignHCenter
icon.height: parent.iconSize Layout.preferredWidth: bottomRow.buttonSize
icon.width: parent.iconSize onClicked: AudioManager.playbackState === Audio.PlayingState ? AudioManager.pause() : AudioManager.play()
flat: true enabled: AudioManager.canPlay
Layout.alignment: Qt.AlignHCenter }
Layout.preferredWidth: parent.buttonSize Controls.Button {
onClicked: AudioManager.skipForward() icon.name: "media-seek-forward"
enabled: AudioManager.canSkipForward 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 { Controls.Button {
icon.name: "media-skip-forward" icon.name: "media-skip-forward"
icon.height: parent.iconSize icon.height: bottomRow.iconSize
icon.width: parent.iconSize icon.width: bottomRow.iconSize
flat: true flat: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignRight
Layout.preferredWidth: parent.buttonSize Layout.preferredWidth: bottomRow.buttonSize
onClicked: AudioManager.next() onClicked: AudioManager.next()
enabled: AudioManager.canGoNext enabled: AudioManager.canGoNext
} }

View File

@ -8,6 +8,7 @@
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14 as Controls import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.12
import org.kde.kirigami 2.14 as Kirigami import org.kde.kirigami 2.14 as Kirigami
import org.kde.kasts.solidextras 1.0 import org.kde.kasts.solidextras 1.0
@ -21,18 +22,33 @@ Kirigami.ApplicationWindow {
minimumWidth: Kirigami.Units.gridUnit * 17 minimumWidth: Kirigami.Units.gridUnit * 17
minimumHeight: Kirigami.Units.gridUnit * 20 minimumHeight: Kirigami.Units.gridUnit * 20
property var miniplayerSize: Kirigami.Units.gridUnit * 3 + Kirigami.Units.gridUnit / 6 property var miniplayerSize: Math.round(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 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 int originalWidth: Kirigami.Units.gridUnit * 10
property var lastFeed: "" property var lastFeed: ""
property string currentPage: "" property string currentPage: ""
property bool isWidescreen: root.width >= root.height property bool isWidescreen: root.width >= root.height
onIsWidescreenChanged: { 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) { function getPage(page) {
switch (page) { switch (page) {
case "QueuePage": return "qrc:/QueuePage.qml"; case "QueuePage": return "qrc:/QueuePage.qml";
@ -55,8 +71,10 @@ Kirigami.ApplicationWindow {
currentPage = SettingsManager.lastOpenedPage currentPage = SettingsManager.lastOpenedPage
pageStack.initialPage = getPage(SettingsManager.lastOpenedPage) pageStack.initialPage = getPage(SettingsManager.lastOpenedPage)
// move mobile handles to toolbar if (Kirigami.Settings.isMobile) {
pageStack.globalToolBar.canContainHandles = true; pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.ToolBar;
pageStack.globalToolBar.showNavigationButtons = Kirigami.ApplicationHeaderStyle.ShowBackButton;
}
// Delete played enclosures if set in settings // Delete played enclosures if set in settings
if (SettingsManager.autoDeleteOnPlayed == 2) { if (SettingsManager.autoDeleteOnPlayed == 2) {
@ -73,93 +91,78 @@ Kirigami.ApplicationWindow {
} }
} }
globalDrawer: Kirigami.GlobalDrawer { globalDrawer: sidebar.item
isMenu: false Loader {
modal: Kirigami.Settings.isMobile id: sidebar
collapsible: !Kirigami.Settings.isMobile active: !Kirigami.Settings.isMobile || root.isWidescreen
header: Kirigami.AbstractApplicationHeader { sourceComponent: Kirigami.GlobalDrawer {
visible: !Kirigami.Settings.isMobile width: 200
} modal: false
isMenu: false
collapsible: !Kirigami.Settings.isMobile
header: Kirigami.AbstractApplicationHeader {}
Component.onCompleted: { Kirigami.Theme.colorSet: Kirigami.Theme.Window
if (!Kirigami.Settings.isMobile) { Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet = Kirigami.Theme.Window;
Kirigami.Theme.inherit = false;
}
}
// make room at the bottom for miniplayer actions: [
handle.anchors.bottomMargin: (( AudioManager.entry && Kirigami.Settings.isMobile ) ? (footerLoader.item.contentY == 0 ? miniplayerSize : 0) : 0) + Kirigami.Units.smallSpacing Kirigami.Action {
handleVisible: Kirigami.Settings.isMobile ? !AudioManager.entry || footerLoader.item.contentY === 0 : false text: i18n("Queue")
showHeaderWhenCollapsed: true iconName: "source-playlist"
actions: [ checked: currentPage == "QueuePage"
Kirigami.Action { onTriggered: {
text: i18n("Queue") pushPage("QueuePage")
iconName: "source-playlist" SettingsManager.lastOpenedPage = "QueuePage" // for persistency
checked: currentPage == "QueuePage" }
onTriggered: { },
pushPage("QueuePage") Kirigami.Action {
SettingsManager.lastOpenedPage = "QueuePage" // for persistency 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 active: !Kirigami.Settings.isMobile
visible: active visible: active
sourceComponent: HeaderBar { sourceComponent: HeaderBar { focus: true }
focus: true
}
} }
// create space at the bottom to show miniplayer without it hiding stuff // create space at the bottom to show miniplayer without it hiding stuff
@ -209,8 +210,32 @@ Kirigami.ApplicationWindow {
sourceComponent: FooterBar { sourceComponent: FooterBar {
contentHeight: root.height * 2 contentHeight: root.height * 2
focus: true 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 // Notification that shows the progress of feed updates

View File

@ -34,6 +34,7 @@
<file alias="NetworkSettingsPage.qml">qml/Settings/NetworkSettingsPage.qml</file> <file alias="NetworkSettingsPage.qml">qml/Settings/NetworkSettingsPage.qml</file>
<file alias="StorageSettingsPage.qml">qml/Settings/StorageSettingsPage.qml</file> <file alias="StorageSettingsPage.qml">qml/Settings/StorageSettingsPage.qml</file>
<file alias="SynchronizationSettingsPage.qml">qml/Settings/SynchronizationSettingsPage.qml</file> <file alias="SynchronizationSettingsPage.qml">qml/Settings/SynchronizationSettingsPage.qml</file>
<file alias="BottomToolbar.qml">qml/BottomToolbar.qml</file>
<file>qtquickcontrols2.conf</file> <file>qtquickcontrols2.conf</file>
<file alias="logo.svg">../kasts.svg</file> <file alias="logo.svg">../kasts.svg</file>
</qresource> </qresource>