Move page actions to bar, and consolidate podcast list and info pages

This commit is contained in:
Devin Lin 2022-09-07 19:03:07 +00:00 committed by Bart De Vries
parent 9860c8b9e5
commit 3a6446dea5
7 changed files with 450 additions and 361 deletions

View File

@ -1,122 +0,0 @@
/**
* SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
* SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.15
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.15
import org.kde.kirigami 2.14 as Kirigami
import org.kde.kasts 1.0
Kirigami.ScrollablePage {
id: page
property var feed
title: i18n("Episode List")
supportsRefreshing: true
onRefreshingChanged: {
if(refreshing) {
updateFeed.run()
}
}
// Overlay dialog box showing options what to do on metered connections
ConnectionCheckAction {
id: updateFeed
function action() {
feed.refresh()
}
function abortAction() {
page.refreshing = false
}
}
// Make sure that this feed is also showing as "refreshing" on FeedListPage
Connections {
target: feed
function onRefreshingChanged(refreshing) {
if(!refreshing)
page.refreshing = refreshing
}
}
actions.main: Kirigami.Action {
iconName: "view-refresh"
text: i18n("Refresh Podcast")
onTriggered: page.refreshing = true
}
contextualActions: [
Kirigami.Action {
iconName: "help-about-symbolic"
text: i18n("Podcast Details")
onTriggered: {
while(pageStack.depth > 2)
pageStack.pop()
pageStack.push("qrc:/FeedDetailsPage.qml", {"feed": feed})
}
}
]
// add the default actions through onCompleted to add them to the ones
// defined above
Component.onCompleted: {
for (var i in entryList.defaultActionList) {
contextualActions.push(entryList.defaultActionList[i]);
}
}
Kirigami.PlaceholderMessage {
visible: entryList.count === 0
width: Kirigami.Units.gridUnit * 20
anchors.centerIn: parent
text: feed.errorId === 0 ? i18n("No Episodes Available") : i18n("Error (%1): %2", feed.errorId, feed.errorString)
icon.name: feed.errorId === 0 ? "" : "data-error"
}
Component {
id: entryListDelegate
GenericEntryDelegate {
listView: entryList
}
}
GenericEntryListView {
id: entryList
visible: count !== 0
reuseItems: true
model: page.feed.entries
delegate: entryListDelegate
// OverlayHeader looks nicer, but seems completely broken when flicking the list
// headerPositioning: ListView.OverlayHeader
header: GenericHeader {
id: headerImage
image: feed.cachedImage
title: feed.name
subtitle: page.feed.authors.length === 0 ? "" : i18nc("by <author(s)>", "by %1", page.feed.authors[0].name)
MouseArea {
anchors.fill: parent
onClicked: {
while(pageStack.depth > 2)
pageStack.pop()
pageStack.push("qrc:/FeedDetailsPage.qml", {"feed": feed})
}
}
}
}
}

View File

@ -23,6 +23,13 @@ Kirigami.ScrollablePage {
padding: 0 // needed to get the inline header to fill the page padding: 0 // needed to get the inline header to fill the page
function openPodcast() {
pushPage("FeedListPage")
SettingsManager.lastOpenedPage = "FeedListPage" // for persistency
lastFeed = entry.feed.url;
pageStack.push("qrc:/FeedDetailsPage.qml", {"feed": entry.feed});
}
// This function is needed to close the EntryPage if it is opened over the // This function is needed to close the EntryPage if it is opened over the
// QueuePage when the episode is removed from the queue (e.g. when the // QueuePage when the episode is removed from the queue (e.g. when the
// episode finishes). // episode finishes).
@ -65,30 +72,149 @@ Kirigami.ScrollablePage {
} }
ColumnLayout { ColumnLayout {
spacing: 0
GenericHeader { GenericHeader {
id: infoHeader id: infoHeader
Layout.fillWidth: true Layout.fillWidth: true
image: entry.cachedImage image: entry.cachedImage
title: entry.title title: entry.title
subtitle: entry.feed.name subtitle: entry.feed.name
clickable: true
onClicked: page.openPodcast()
}
// header actions
Controls.Control {
Layout.fillWidth: true
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.smallSpacing
topPadding: Kirigami.Units.smallSpacing
background: Rectangle {
color: Kirigami.Theme.alternateBackgroundColor
Kirigami.Separator {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
}
}
contentItem: Kirigami.ActionToolBar {
alignment: Qt.AlignLeft
background: Item {}
actions: [
Kirigami.Action {
text: !entry.enclosure ? i18n("Open in Browser") :
(entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded) ? i18n("Download") :
entry.enclosure.status === Enclosure.Downloading ? i18n("Cancel Download") :
!entry.queueStatus ? i18n("Delete Download") :
(AudioManager.entry === entry && AudioManager.playbackState === Audio.PlayingState) ? i18n("Pause") :
i18n("Play")
icon.name: !entry.enclosure ? "globe" :
(entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded) ? "download" :
entry.enclosure.status === Enclosure.Downloading ? "edit-delete-remove" :
!entry.queueStatus ? "delete" :
(AudioManager.entry === entry && AudioManager.playbackState === Audio.PlayingState) ? "media-playback-pause" :
"media-playback-start"
onTriggered: {
if (!entry.enclosure) {
Qt.openUrlExternally(entry.link)
} else if (entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded) {
downloadOverlay.entry = entry;
downloadOverlay.run();
} else if (entry.enclosure.status === Enclosure.Downloading) {
entry.enclosure.cancelDownload()
} else if (!entry.queueStatus) {
entry.enclosure.deleteFile()
} else {
if(AudioManager.entry === entry && AudioManager.playbackState === Audio.PlayingState) {
AudioManager.pause()
} else {
AudioManager.entry = entry
AudioManager.play()
}
}
}
},
Kirigami.Action {
text: !entry.queueStatus ? i18n("Add to Queue") : i18n("Remove from Queue")
icon.name: !entry.queueStatus ? "media-playlist-append" : "list-remove"
visible: entry.enclosure || entry.queueStatus
onTriggered: {
if(!entry.queueStatus) {
entry.queueStatus = true
} else {
// first change to next track if this one is playing
if (entry.hasEnclosure && entry === AudioManager.entry) {
AudioManager.next()
}
entry.queueStatus = false
}
}
},
Kirigami.Action {
text: i18n("Delete Download")
icon.name: "delete"
onTriggered: entry.enclosure.deleteFile();
visible: entry.enclosure && ((entry.enclosure.status === Enclosure.Downloaded && entry.queueStatus) || entry.enclosure.status === Enclosure.PartiallyDownloaded)
},
Kirigami.Action {
text: i18n("Reset Play Position")
visible: entry.enclosure && entry.enclosure.playPosition > 1000
onTriggered: entry.enclosure.playPosition = 0
displayHint: Kirigami.DisplayHint.AlwaysHide
},
Kirigami.Action {
text: entry.read ? i18n("Mark as Unplayed") : i18n("Mark as Played")
displayHint: Kirigami.DisplayHint.AlwaysHide
onTriggered: {
entry.read = !entry.read
}
},
Kirigami.Action {
text: entry.new ? i18n("Remove \"New\" Label") : i18n("Label as \"New\"")
displayHint: Kirigami.DisplayHint.AlwaysHide
onTriggered: {
entry.new = !entry.new
}
},
Kirigami.Action {
text: i18n("Open Podcast")
displayHint: Kirigami.DisplayHint.AlwaysHide
onTriggered: page.openPodcast()
}
]
}
} }
TextEdit { TextEdit {
id: textLabel id: textLabel
Layout.margins: Kirigami.Units.gridUnit Layout.topMargin: Kirigami.Units.gridUnit
Layout.leftMargin: Kirigami.Units.gridUnit
Layout.rightMargin: Kirigami.Units.gridUnit
Layout.bottomMargin: Kirigami.Units.gridUnit
Layout.fillWidth: true
Layout.fillHeight: true
readOnly: true readOnly: true
selectByMouse: !Kirigami.Settings.isMobile selectByMouse: !Kirigami.Settings.isMobile
text: page.entry.content text: page.entry.content
baseUrl: page.entry.baseUrl baseUrl: page.entry.baseUrl
textFormat: Text.RichText textFormat: Text.RichText
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.fillHeight: true
onLinkActivated: Qt.openUrlExternally(link)
onWidthChanged: { text = entry.adjustedContent(width, font.pixelSize) }
font.pointSize: SettingsManager && !(SettingsManager.articleFontUseSystem) ? SettingsManager.articleFontSize : Kirigami.Theme.defaultFont.pointSize font.pointSize: SettingsManager && !(SettingsManager.articleFontUseSystem) ? SettingsManager.articleFontSize : Kirigami.Theme.defaultFont.pointSize
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
onLinkActivated: Qt.openUrlExternally(link)
onWidthChanged: { text = entry.adjustedContent(width, font.pixelSize) }
} }
ListView { ListView {
visible: count !== 0 visible: count !== 0
Layout.fillWidth: true Layout.fillWidth: true
@ -105,19 +231,26 @@ Kirigami.ScrollablePage {
entry: page.entry entry: page.entry
} }
} }
RowLayout {
Controls.Button {
Layout.leftMargin: Kirigami.Units.gridUnit Layout.leftMargin: Kirigami.Units.gridUnit
Layout.rightMargin: Kirigami.Units.gridUnit Layout.rightMargin: Kirigami.Units.gridUnit
Layout.bottomMargin: Kirigami.Units.gridUnit Layout.bottomMargin: Kirigami.Units.gridUnit
visible: entry.hasEnclosure visible: entry.hasEnclosure
spacing: Kirigami.Units.smallSpacing
TextEdit { text: i18n("Copy Episode Download URL")
readOnly: true height: enclosureUrl.height
textFormat:TextEdit.RichText width: enclosureUrl.height
text: i18n("Episode Download URL:") icon.name: "edit-copy"
wrapMode: TextEdit.Wrap
color: Kirigami.Theme.textColor onClicked: {
applicationWindow().showPassiveNotification(i18n("Link copied"));
enclosureUrl.selectAll();
enclosureUrl.copy();
enclosureUrl.deselect();
} }
// copy url from this invisible textedit
TextEdit { TextEdit {
id: enclosureUrl id: enclosureUrl
visible: false visible: false
@ -126,103 +259,6 @@ Kirigami.ScrollablePage {
text: entry.hasEnclosure ? entry.enclosure.url : "" text: entry.hasEnclosure ? entry.enclosure.url : ""
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
} }
Controls.Button {
height: enclosureUrl.height
width: enclosureUrl.height
icon.name: "edit-copy"
onClicked: {
enclosureUrl.selectAll();
enclosureUrl.copy();
enclosureUrl.deselect();
}
}
} }
} }
actions.main: Kirigami.Action {
text: !entry.enclosure ? i18n("Open in Browser") :
(entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded) ? i18n("Download") :
entry.enclosure.status === Enclosure.Downloading ? i18n("Cancel Download") :
!entry.queueStatus ? i18n("Delete Download") :
(AudioManager.entry === entry && AudioManager.playbackState === Audio.PlayingState) ? i18n("Pause") :
i18n("Play")
icon.name: !entry.enclosure ? "globe" :
(entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded) ? "download" :
entry.enclosure.status === Enclosure.Downloading ? "edit-delete-remove" :
!entry.queueStatus ? "delete" :
(AudioManager.entry === entry && AudioManager.playbackState === Audio.PlayingState) ? "media-playback-pause" :
"media-playback-start"
onTriggered: {
if (!entry.enclosure) {
Qt.openUrlExternally(entry.link)
} else if (entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded) {
downloadOverlay.entry = entry;
downloadOverlay.run();
} else if (entry.enclosure.status === Enclosure.Downloading) {
entry.enclosure.cancelDownload()
} else if (!entry.queueStatus) {
entry.enclosure.deleteFile()
} else {
if(AudioManager.entry === entry && AudioManager.playbackState === Audio.PlayingState) {
AudioManager.pause()
} else {
AudioManager.entry = entry
AudioManager.play()
}
}
}
}
actions.left: Kirigami.Action {
text: !entry.queueStatus ? i18n("Add to Queue") : i18n("Remove from Queue")
icon.name: !entry.queueStatus ? "media-playlist-append" : "list-remove"
visible: entry.enclosure || entry.queueStatus
onTriggered: {
if(!entry.queueStatus) {
entry.queueStatus = true
} else {
// first change to next track if this one is playing
if (entry.hasEnclosure && entry === AudioManager.entry) {
AudioManager.next()
}
entry.queueStatus = false
}
}
}
actions.right: Kirigami.Action {
text: i18n("Delete Download")
icon.name: "delete"
onTriggered: entry.enclosure.deleteFile();
visible: entry.enclosure && ((entry.enclosure.status === Enclosure.Downloaded && entry.queueStatus) || entry.enclosure.status === Enclosure.PartiallyDownloaded)
}
contextualActions: [
Kirigami.Action {
text: i18n("Reset Play Position")
visible: entry.enclosure && entry.enclosure.playPosition > 1000
onTriggered: entry.enclosure.playPosition = 0
},
Kirigami.Action {
text: entry.read ? i18n("Mark as Unplayed") : i18n("Mark as Played")
onTriggered: {
entry.read = !entry.read
}
},
Kirigami.Action {
text: entry.new ? i18n("Remove \"New\" Label") : i18n("Label as \"New\"")
onTriggered: {
entry.new = !entry.new
}
},
Kirigami.Action {
text: i18n("Open Podcast")
onTriggered: {
pushPage("FeedListPage")
SettingsManager.lastOpenedPage = "FeedListPage" // for persistency
lastFeed = entry.feed.url;
pageStack.push("qrc:/EntryListPage.qml", {"feed": entry.feed});
}
}
]
} }

View File

@ -20,145 +20,304 @@ Kirigami.ScrollablePage {
property bool isSubscribed: true property bool isSubscribed: true
property string author: isSubscribed ? (page.feed.authors.length === 0 ? "" : page.feed.authors[0].name) : feed.author property string author: isSubscribed ? (page.feed.authors.length === 0 ? "" : page.feed.authors[0].name) : feed.author
property bool showMoreInfo: false
title: i18n("Podcast Details") title: i18n("Podcast Details")
header: GenericHeader { supportsRefreshing: true
id: headerImage
image: isSubscribed ? feed.cachedImage : feed.image onRefreshingChanged: {
title: isSubscribed ? feed.name : feed.title if (refreshing) {
subtitle: author !== "" ? i18nc("by <Author(s)>", "by %1", author) : "" updateFeed.run()
Controls.Button {
text: enabled ? i18n("Subscribe") : i18n("Subscribed")
icon.name: "kt-add-feeds"
visible: !isSubscribed
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: Kirigami.Units.largeSpacing
anchors.topMargin: Kirigami.Units.largeSpacing
onClicked: {
DataManager.addFeed(feed.url)
}
enabled: !DataManager.feedExists(feed.url)
} }
} }
ColumnLayout { // Overlay dialog box showing options what to do on metered connections
width: parent.width ConnectionCheckAction {
TextEdit { id: updateFeed
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile function action() {
textFormat:TextEdit.RichText feed.refresh()
text: feed.description
font.pointSize: Math.round(Kirigami.Theme.defaultFont.pointSize * 1.2)
wrapMode: Text.WordWrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
} }
TextEdit {
readOnly: true function abortAction() {
selectByMouse: !Kirigami.Settings.isMobile page.refreshing = false
textFormat:TextEdit.RichText
text: i18nc("by <Author(s)>", "by %1", author)
visible: author !== ""
wrapMode: Text.WordWrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
} }
Item { }
Layout.fillWidth: true
Layout.preferredHeight: Math.max(feedUrlLayout.height, feedUrlCopyButton.width) // Make sure that this feed is also showing as "refreshing" on FeedListPage
RowLayout { Connections {
id: feedUrlLayout target: feed
anchors.left: parent.left function onRefreshingChanged(refreshing) {
anchors.right: feedUrlCopyButton.left if(!refreshing)
anchors.verticalCenter: parent.verticalCenter page.refreshing = refreshing
spacing: Kirigami.Units.smallSpacing
TextEdit {
Layout.alignment: Qt.AlignTop
readOnly: true
textFormat:TextEdit.RichText
text: i18n("Podcast URL:")
wrapMode: TextEdit.Wrap
color: Kirigami.Theme.textColor
}
TextEdit {
id: feedUrl
Layout.alignment: Qt.AlignTop
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: "<a href='%1'>%1</a>".arg(feed.url)
wrapMode: TextEdit.Wrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
}
}
Controls.Button {
anchors.right: parent.right
anchors.top: parent.top
anchors.leftMargin: Kirigami.Units.smallSpacing
id: feedUrlCopyButton
icon.name: "edit-copy"
onClicked: {
feedUrl.selectAll();
feedUrl.copy();
feedUrl.deselect();
}
}
} }
RowLayout { }
spacing: Kirigami.Units.smallSpacing
TextEdit { // add the default actions through onCompleted to add them to the ones
Layout.alignment: Qt.AlignTop // defined above
readOnly: true Component.onCompleted: {
textFormat:TextEdit.RichText for (var i in entryList.defaultActionList) {
text: i18n("Weblink:") contextualActions.push(entryList.defaultActionList[i]);
wrapMode: TextEdit.Wrap }
color: Kirigami.Theme.textColor }
Component {
id: entryListDelegate
GenericEntryDelegate {
listView: entryList
}
}
ListModel {
id: emptyListModel
}
GenericEntryListView {
id: entryList
visible: true
reuseItems: true
currentIndex: -1
model: page.feed.entries ? page.feed.entries : emptyListModel
delegate: entryListDelegate
// OverlayHeader looks nicer, but seems completely broken when flicking the list
//headerPositioning: ListView.OverlayHeader
header: ColumnLayout {
id: headerColumn
height: (isSubscribed && entryList.count > 0) ? implicitHeight : entryList.height
width: entryList.width
spacing: 0
property real headerOverlayProgress: Math.min(1, Math.abs(entryList.contentY) / headerColumn.height)
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window
GenericHeader {
id: headerImage
Layout.fillWidth: true
image: isSubscribed ? feed.cachedImage : feed.image
title: isSubscribed ? feed.name : feed.title
subtitle: (!page.feed.authors || page.feed.authors.length === 0) ? "" : i18nc("by <author(s)>", "by %1", page.feed.authors[0].name)
} }
TextEdit { // header actions
readOnly: true Controls.Control {
Layout.alignment: Qt.AlignTop
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: "<a href='%1'>%1</a>".arg(feed.link)
onLinkActivated: Qt.openUrlExternally(link)
wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
color: Kirigami.Theme.textColor
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.smallSpacing
topPadding: Kirigami.Units.smallSpacing
background: Rectangle {
color: Kirigami.Theme.alternateBackgroundColor
}
contentItem: Kirigami.ActionToolBar {
alignment: Qt.AlignLeft
background: Item {}
// HACK: ActionToolBar loads buttons dynamically, and so the height calculation
// changes the position
onHeightChanged: entryList.contentY = entryList.originY
actions: [
Kirigami.Action {
visible: isSubscribed
iconName: "view-refresh"
text: i18n("Refresh Podcast")
onTriggered: page.refreshing = true
},
Kirigami.Action {
iconName: "kt-add-feeds"
text: enabled ? i18n("Subscribe") : i18n("Subscribed")
enabled: !DataManager.feedExists(feed.url)
visible: !isSubscribed
onTriggered: DataManager.addFeed(feed.url)
},
Kirigami.Action {
iconName: "help-about-symbolic"
text: i18n("Show Details")
checkable: true
onCheckedChanged: {
showMoreInfo = checked;
}
}
]
}
}
Kirigami.Separator { Layout.fillWidth: true }
// podcast description
Controls.Control {
Layout.fillHeight: !isSubscribed
Layout.fillWidth: true
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
topPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
// HACK: opening more info changes the position of the header
onHeightChanged: entryList.contentY = entryList.originY
background: Rectangle {
color: Kirigami.Theme.backgroundColor
}
contentItem: ColumnLayout {
spacing: Kirigami.Units.smallSpacing
Controls.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
textFormat: TextEdit.RichText
text: feed.description
font.pointSize: Kirigami.Theme.defaultFont.pointSize
wrapMode: Text.WordWrap
color: Kirigami.Theme.textColor
lineHeight: 1.2
}
Item {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.max(feedUrlLayout.height, feedUrlCopyButton.width)
visible: page.showMoreInfo
height: visible ? implicitHeight : 0
RowLayout {
id: feedUrlLayout
anchors.left: parent.left
anchors.right: feedUrlCopyButton.left
anchors.verticalCenter: parent.verticalCenter
spacing: Kirigami.Units.smallSpacing
TextEdit {
Layout.alignment: Qt.AlignTop
readOnly: true
textFormat:TextEdit.RichText
text: i18n("Podcast URL:")
wrapMode: TextEdit.Wrap
color: Kirigami.Theme.textColor
}
TextEdit {
id: feedUrl
Layout.alignment: Qt.AlignTop
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: "<a href='%1'>%1</a>".arg(feed.url)
wrapMode: TextEdit.Wrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
}
}
Controls.Button {
id: feedUrlCopyButton
anchors.right: parent.right
anchors.top: parent.top
anchors.leftMargin: Kirigami.Units.smallSpacing
icon.name: "edit-copy"
onClicked: {
feedUrl.selectAll();
feedUrl.copy();
feedUrl.deselect();
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
visible: page.showMoreInfo
height: visible ? implicitHeight : 0
spacing: Kirigami.Units.smallSpacing
TextEdit {
Layout.alignment: Qt.AlignTop
readOnly: true
textFormat: TextEdit.RichText
text: i18n("Weblink:")
wrapMode: TextEdit.Wrap
color: Kirigami.Theme.textColor
}
TextEdit {
readOnly: true
Layout.alignment: Qt.AlignTop
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: "<a href='%1'>%1</a>".arg(feed.link)
onLinkActivated: Qt.openUrlExternally(link)
wrapMode: Text.WordWrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
}
}
TextEdit {
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
visible: isSubscribed && page.showMoreInfo
height: visible ? implicitHeight : 0
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: isSubscribed ? i18n("Subscribed since: %1", feed.subscribed.toLocaleString(Qt.locale(), Locale.ShortFormat)) : ""
wrapMode: Text.WordWrap
color: Kirigami.Theme.textColor
}
TextEdit {
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
visible: isSubscribed && page.showMoreInfo
height: visible ? implicitHeight : 0
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: isSubscribed ? i18n("Last Updated: %1", feed.lastUpdated.toLocaleString(Qt.locale(), Locale.ShortFormat)) : ""
wrapMode: Text.WordWrap
color: Kirigami.Theme.textColor
}
TextEdit {
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
visible: isSubscribed && page.showMoreInfo
height: visible ? implicitHeight : 0
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: i18np("1 Episode", "%1 Episodes", feed.entryCount) + ", " + i18np("1 Unplayed", "%1 Unplayed", feed.unreadEntryCount)
wrapMode: Text.WordWrap
color: Kirigami.Theme.textColor
}
Item { Layout.fillHeight: true }
}
}
Kirigami.Separator { Layout.fillWidth: true }
Item {
Layout.fillHeight: true
Layout.fillWidth: true
height: visible ? implicitHeight : 0
visible: entryList.count === 0 && isSubscribed
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
width: Kirigami.Units.gridUnit * 20
text: feed.errorId === 0 ? i18n("No Episodes Available") : i18n("Error (%1): %2", feed.errorId, feed.errorString)
icon.name: feed.errorId === 0 ? "" : "data-error"
}
} }
}
TextEdit {
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: isSubscribed ? i18n("Subscribed since: %1", feed.subscribed.toLocaleString(Qt.locale(), Locale.ShortFormat)) : ""
visible: isSubscribed
wrapMode: Text.WordWrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
}
TextEdit {
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: isSubscribed ? i18n("Last Updated: %1", feed.lastUpdated.toLocaleString(Qt.locale(), Locale.ShortFormat)) : ""
visible: isSubscribed
wrapMode: Text.WordWrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
}
TextEdit {
readOnly: true
selectByMouse: !Kirigami.Settings.isMobile
textFormat:TextEdit.RichText
text: i18np("1 Episode", "%1 Episodes", feed.entryCount) + ", " + i18np("1 Unplayed", "%1 Unplayed", feed.unreadEntryCount)
visible: isSubscribed
wrapMode: Text.WordWrap
Layout.fillWidth: true
color: Kirigami.Theme.textColor
} }
} }
} }

View File

@ -229,7 +229,7 @@ Controls.ItemDelegate {
lastFeed = feed.url lastFeed = feed.url
if (pageStack.depth > 1) if (pageStack.depth > 1)
pageStack.pop(); pageStack.pop();
pageStack.push("qrc:/EntryListPage.qml", {"feed": feed}) pageStack.push("qrc:/FeedDetailsPage.qml", {"feed": feed})
} }
Controls.ToolTip { Controls.ToolTip {

View File

@ -11,7 +11,7 @@ import Qt.labs.platform 1.1
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import QtQml.Models 2.15 import QtQml.Models 2.15
import org.kde.kirigami 2.12 as Kirigami import org.kde.kirigami 2.19 as Kirigami
import org.kde.kasts 1.0 import org.kde.kasts 1.0
@ -57,11 +57,13 @@ Kirigami.ScrollablePage {
Kirigami.Action { Kirigami.Action {
text: i18n("Import Podcasts...") text: i18n("Import Podcasts...")
iconName: "document-import" iconName: "document-import"
displayHint: Kirigami.DisplayHint.AlwaysHide
onTriggered: importDialog.open() onTriggered: importDialog.open()
}, },
Kirigami.Action { Kirigami.Action {
text: i18n("Export Podcasts...") text: i18n("Export Podcasts...")
iconName: "document-export" iconName: "document-export"
displayHint: Kirigami.DisplayHint.AlwaysHide
onTriggered: exportDialog.open() onTriggered: exportDialog.open()
} }
] ]
@ -106,6 +108,7 @@ Kirigami.ScrollablePage {
GridView { GridView {
id: feedList id: feedList
currentIndex: -1
visible: count !== 0 visible: count !== 0
clip: true clip: true

View File

@ -14,11 +14,16 @@ import org.kde.kirigami 2.14 as Kirigami
import org.kde.kasts 1.0 import org.kde.kasts 1.0
Item { Item {
id: root
required property string image required property string image
required property string title required property string title
property string subtitle: "" property string subtitle: ""
property var headerHeight: Kirigami.Units.gridUnit * 8 property var headerHeight: Kirigami.Units.gridUnit * 8
property bool clickable: false
signal clicked()
implicitHeight: headerHeight implicitHeight: headerHeight
implicitWidth: parent.width implicitWidth: parent.width
@ -41,6 +46,15 @@ Item {
color:"#87000000" //RGBA, but first value is actually the alpha channel color:"#87000000" //RGBA, but first value is actually the alpha channel
} }
MouseArea {
anchors.fill: parent
onClicked: {
if (root.clickable) {
root.clicked();
}
}
}
RowLayout { RowLayout {
property int size: Kirigami.Units.gridUnit * 6 property int size: Kirigami.Units.gridUnit * 6
property int margin: Kirigami.Units.gridUnit * 1 property int margin: Kirigami.Units.gridUnit * 1

View File

@ -3,7 +3,6 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file alias="main.qml">qml/main.qml</file> <file alias="main.qml">qml/main.qml</file>
<file alias="EntryListPage.qml">qml/EntryListPage.qml</file>
<file alias="FeedListPage.qml">qml/FeedListPage.qml</file> <file alias="FeedListPage.qml">qml/FeedListPage.qml</file>
<file alias="EntryPage.qml">qml/EntryPage.qml</file> <file alias="EntryPage.qml">qml/EntryPage.qml</file>
<file alias="AboutPage.qml">qml/Settings/AboutPage.qml</file> <file alias="AboutPage.qml">qml/Settings/AboutPage.qml</file>