mirror of https://github.com/KDE/kasts.git
Implement backend to allow Feed list sort and search
The current list of things to sort on (ascending and descending), includes: - unplayed episodes - new episodes - favorite episodes - title (alphabetical) For the first three categories, the value of the sort quantity will be shown in the upper right corner of the delegate. BUG: 471012 CCBUG: 459885
This commit is contained in:
parent
4b1fe5e3f9
commit
c3ca038af7
|
@ -267,6 +267,8 @@ if(ANDROID)
|
|||
view-sort
|
||||
view-sort-descending
|
||||
view-sort-ascending
|
||||
view-sort-descending-name
|
||||
view-sort-ascending-name
|
||||
)
|
||||
else()
|
||||
target_link_libraries(kasts PRIVATE Qt::Widgets)
|
||||
|
|
|
@ -196,28 +196,6 @@ int DataManager::entryCount(const Feed *feed) const
|
|||
return m_entrymap[feed->url()].count();
|
||||
}
|
||||
|
||||
int DataManager::newEntryCount(const Feed *feed) const
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND new=1;"));
|
||||
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
return -1;
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
int DataManager::favoriteEntryCount(const Feed *feed) const
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND favorite=1;"));
|
||||
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
return -1;
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
void DataManager::removeFeed(Feed *feed)
|
||||
{
|
||||
QList<Feed *> feeds;
|
||||
|
|
|
@ -37,8 +37,6 @@ public:
|
|||
QStringList getIdList(const Feed *feed) const;
|
||||
int entryCount(const int feed_index) const;
|
||||
int entryCount(const Feed *feed) const;
|
||||
int newEntryCount(const Feed *feed) const;
|
||||
int favoriteEntryCount(const Feed *feed) const;
|
||||
Q_INVOKABLE void addFeed(const QString &url);
|
||||
void addFeed(const QString &url, const bool fetch);
|
||||
void addFeeds(const QStringList &urls);
|
||||
|
|
43
src/feed.cpp
43
src/feed.cpp
|
@ -44,6 +44,8 @@ Feed::Feed(const QString &feedurl)
|
|||
|
||||
updateAuthors();
|
||||
updateUnreadEntryCountFromDB();
|
||||
updateNewEntryCountFromDB();
|
||||
updateFavoriteEntryCountFromDB();
|
||||
|
||||
connect(&Fetcher::instance(), &Fetcher::feedUpdateStatusChanged, this, [this](const QString &url, bool status) {
|
||||
if (url == m_url) {
|
||||
|
@ -54,11 +56,26 @@ Feed::Feed(const QString &feedurl)
|
|||
if (url == m_url) {
|
||||
Q_EMIT entryCountChanged();
|
||||
updateUnreadEntryCountFromDB();
|
||||
Q_EMIT DataManager::instance().unreadEntryCountChanged(m_url);
|
||||
Q_EMIT unreadEntryCountChanged();
|
||||
Q_EMIT DataManager::instance().newEntryCountChanged(m_url);
|
||||
Q_EMIT newEntryCountChanged();
|
||||
setErrorId(0);
|
||||
setErrorString(QLatin1String(""));
|
||||
}
|
||||
});
|
||||
connect(&DataManager::instance(), &DataManager::newEntryCountChanged, this, [this](const QString &url) {
|
||||
if (url == m_url) {
|
||||
updateNewEntryCountFromDB();
|
||||
Q_EMIT newEntryCountChanged();
|
||||
}
|
||||
});
|
||||
connect(&DataManager::instance(), &DataManager::favoriteEntryCountChanged, this, [this](const QString &url) {
|
||||
if (url == m_url) {
|
||||
updateFavoriteEntryCountFromDB();
|
||||
Q_EMIT favoriteEntryCountChanged();
|
||||
}
|
||||
});
|
||||
connect(&Fetcher::instance(),
|
||||
&Fetcher::error,
|
||||
this,
|
||||
|
@ -141,6 +158,28 @@ void Feed::updateUnreadEntryCountFromDB()
|
|||
m_unreadEntryCount = query.value(0).toInt();
|
||||
}
|
||||
|
||||
void Feed::updateNewEntryCountFromDB()
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND new=1;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_url);
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
m_newEntryCount = -1;
|
||||
m_newEntryCount = query.value(0).toInt();
|
||||
}
|
||||
|
||||
void Feed::updateFavoriteEntryCountFromDB()
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND favorite=1;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_url);
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
m_favoriteEntryCount = -1;
|
||||
m_favoriteEntryCount = query.value(0).toInt();
|
||||
}
|
||||
|
||||
QString Feed::url() const
|
||||
{
|
||||
return m_url;
|
||||
|
@ -218,12 +257,12 @@ int Feed::unreadEntryCount() const
|
|||
|
||||
int Feed::newEntryCount() const
|
||||
{
|
||||
return DataManager::instance().newEntryCount(this);
|
||||
return m_newEntryCount;
|
||||
}
|
||||
|
||||
int Feed::favoriteEntryCount() const
|
||||
{
|
||||
return DataManager::instance().favoriteEntryCount(this);
|
||||
return m_favoriteEntryCount;
|
||||
}
|
||||
|
||||
bool Feed::refreshing() const
|
||||
|
|
|
@ -111,6 +111,8 @@ Q_SIGNALS:
|
|||
|
||||
private:
|
||||
void updateUnreadEntryCountFromDB();
|
||||
void updateNewEntryCountFromDB();
|
||||
void updateFavoriteEntryCountFromDB();
|
||||
|
||||
QString m_url;
|
||||
QString m_name;
|
||||
|
@ -127,6 +129,8 @@ private:
|
|||
int m_errorId;
|
||||
QString m_errorString;
|
||||
int m_unreadEntryCount = -1;
|
||||
int m_newEntryCount = -1;
|
||||
int m_favoriteEntryCount = -1;
|
||||
|
||||
EntriesProxyModel *m_entries;
|
||||
|
||||
|
|
|
@ -28,22 +28,10 @@ FeedsModel::FeedsModel(QObject *parent)
|
|||
beginRemoveRows(QModelIndex(), index, index);
|
||||
endRemoveRows();
|
||||
});
|
||||
connect(&DataManager::instance(), &DataManager::unreadEntryCountChanged, this, [=](const QString &url) {
|
||||
for (int i = 0; i < rowCount(QModelIndex()); i++) {
|
||||
if (data(index(i, 0), UrlRole).toString() == url) {
|
||||
Q_EMIT dataChanged(index(i, 0), index(i, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [=](const QString &url) {
|
||||
for (int i = 0; i < rowCount(QModelIndex()); i++) {
|
||||
if (data(index(i, 0), UrlRole).toString() == url) {
|
||||
Q_EMIT dataChanged(index(i, 0), index(i, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(&DataManager::instance(), &DataManager::unreadEntryCountChanged, this, &FeedsModel::triggerFeedUpdate);
|
||||
connect(&DataManager::instance(), &DataManager::newEntryCountChanged, this, &FeedsModel::triggerFeedUpdate);
|
||||
connect(&DataManager::instance(), &DataManager::favoriteEntryCountChanged, this, &FeedsModel::triggerFeedUpdate);
|
||||
connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, &FeedsModel::triggerFeedUpdate);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> FeedsModel::roleNames() const
|
||||
|
@ -53,6 +41,8 @@ QHash<int, QByteArray> FeedsModel::roleNames() const
|
|||
{UrlRole, "url"},
|
||||
{TitleRole, "title"},
|
||||
{UnreadCountRole, "unreadCount"},
|
||||
{NewCountRole, "newCount"},
|
||||
{FavoriteCountRole, "favoriteCount"},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -74,7 +64,21 @@ QVariant FeedsModel::data(const QModelIndex &index, int role) const
|
|||
return QVariant::fromValue(DataManager::instance().getFeed(index.row())->name());
|
||||
case UnreadCountRole:
|
||||
return QVariant::fromValue(DataManager::instance().getFeed(index.row())->unreadEntryCount());
|
||||
case NewCountRole:
|
||||
return QVariant::fromValue(DataManager::instance().getFeed(index.row())->newEntryCount());
|
||||
case FavoriteCountRole:
|
||||
return QVariant::fromValue(DataManager::instance().getFeed(index.row())->favoriteEntryCount());
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedsModel::triggerFeedUpdate(const QString &url)
|
||||
{
|
||||
for (int i = 0; i < rowCount(QModelIndex()); i++) {
|
||||
if (data(index(i, 0), UrlRole).toString() == url) {
|
||||
Q_EMIT dataChanged(index(i, 0), index(i, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ public:
|
|||
UrlRole,
|
||||
TitleRole,
|
||||
UnreadCountRole,
|
||||
NewCountRole,
|
||||
FavoriteCountRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
|
@ -31,4 +33,7 @@ public:
|
|||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
|
||||
private:
|
||||
void triggerFeedUpdate(const QString &url);
|
||||
};
|
||||
|
|
|
@ -6,26 +6,154 @@
|
|||
|
||||
#include "models/feedsproxymodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
FeedsProxyModel::FeedsProxyModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
m_feedsModel = new FeedsModel(this);
|
||||
setSourceModel(m_feedsModel);
|
||||
setDynamicSortFilter(true);
|
||||
sort(0, Qt::AscendingOrder);
|
||||
sort(0);
|
||||
}
|
||||
|
||||
bool FeedsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
QString leftTitle = sourceModel()->data(left, FeedsModel::TitleRole).toString();
|
||||
QString rightTitle = sourceModel()->data(right, FeedsModel::TitleRole).toString();
|
||||
|
||||
if (m_currentSort == SortType::UnreadDescending || m_currentSort == SortType::UnreadAscending) {
|
||||
int leftUnreadCount = sourceModel()->data(left, FeedsModel::UnreadCountRole).toInt();
|
||||
int rightUnreadCount = sourceModel()->data(right, FeedsModel::UnreadCountRole).toInt();
|
||||
|
||||
if (leftUnreadCount == rightUnreadCount) {
|
||||
return QString::localeAwareCompare(leftTitle, rightTitle) < 0;
|
||||
} else {
|
||||
if (leftUnreadCount != rightUnreadCount) {
|
||||
if (m_currentSort == SortType::UnreadDescending) {
|
||||
return leftUnreadCount > rightUnreadCount;
|
||||
} else {
|
||||
return leftUnreadCount < rightUnreadCount;
|
||||
}
|
||||
}
|
||||
} else if (m_currentSort == SortType::NewDescending || m_currentSort == SortType::NewAscending) {
|
||||
int leftNewCount = sourceModel()->data(left, FeedsModel::NewCountRole).toInt();
|
||||
int rightNewCount = sourceModel()->data(right, FeedsModel::NewCountRole).toInt();
|
||||
|
||||
if (leftNewCount != rightNewCount) {
|
||||
if (m_currentSort == SortType::NewDescending) {
|
||||
return leftNewCount > rightNewCount;
|
||||
} else {
|
||||
return leftNewCount < rightNewCount;
|
||||
}
|
||||
}
|
||||
} else if (m_currentSort == SortType::FavoriteDescending || m_currentSort == SortType::FavoriteAscending) {
|
||||
int leftFavoriteCount = sourceModel()->data(left, FeedsModel::FavoriteCountRole).toInt();
|
||||
int rightFavoriteCount = sourceModel()->data(right, FeedsModel::FavoriteCountRole).toInt();
|
||||
|
||||
if (leftFavoriteCount != rightFavoriteCount) {
|
||||
if (m_currentSort == SortType::FavoriteDescending) {
|
||||
return leftFavoriteCount > rightFavoriteCount;
|
||||
} else {
|
||||
return leftFavoriteCount < rightFavoriteCount;
|
||||
}
|
||||
}
|
||||
} else if (m_currentSort == SortType::TitleDescending) {
|
||||
return QString::localeAwareCompare(leftTitle, rightTitle) > 0;
|
||||
}
|
||||
|
||||
// In case there is a "tie" always use ascending alphabetical ordering
|
||||
return QString::localeAwareCompare(leftTitle, rightTitle) < 0;
|
||||
}
|
||||
|
||||
bool FeedsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
bool found = m_searchFilter.isEmpty();
|
||||
if (!m_searchFilter.isEmpty()) {
|
||||
if (sourceModel()->data(index, FeedsModel::Roles::TitleRole).value<QString>().contains(m_searchFilter, Qt::CaseInsensitive)) {
|
||||
found |= true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
QString FeedsProxyModel::searchFilter() const
|
||||
{
|
||||
return m_searchFilter;
|
||||
}
|
||||
|
||||
FeedsProxyModel::SortType FeedsProxyModel::sortType() const
|
||||
{
|
||||
return m_currentSort;
|
||||
}
|
||||
|
||||
void FeedsProxyModel::setSearchFilter(const QString &searchString)
|
||||
{
|
||||
if (searchString != m_searchFilter) {
|
||||
beginResetModel();
|
||||
m_searchFilter = searchString;
|
||||
endResetModel();
|
||||
|
||||
Q_EMIT searchFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedsProxyModel::setSortType(SortType type)
|
||||
{
|
||||
if (type != m_currentSort) {
|
||||
m_currentSort = type;
|
||||
|
||||
// HACK: get the list re-sorted with a custom lessThan implementation
|
||||
sort(-1);
|
||||
sort(0);
|
||||
|
||||
Q_EMIT sortTypeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString FeedsProxyModel::getSortName(SortType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case SortType::UnreadDescending:
|
||||
return i18nc("@label:chooser Sort podcasts by decreasing number of unplayed episodes", "Unplayed count: descending");
|
||||
case SortType::UnreadAscending:
|
||||
return i18nc("@label:chooser Sort podcasts by increasing number of unplayed episodes", "Unplayed count: ascending");
|
||||
case SortType::NewDescending:
|
||||
return i18nc("@label:chooser Sort podcasts by decreasing number of new episodes", "New count: descending");
|
||||
case SortType::NewAscending:
|
||||
return i18nc("@label:chooser Sort podcasts by increasing number of new episodes", "New count: ascending");
|
||||
case SortType::FavoriteDescending:
|
||||
return i18nc("@label:chooser Sort podcasts by decreasing number of favorites", "Favorite count: descending");
|
||||
case SortType::FavoriteAscending:
|
||||
return i18nc("@label:chooser Sort podcasts by increasing number of favorites", "Favorite count: ascending");
|
||||
case SortType::TitleAscending:
|
||||
return i18nc("@label:chooser Sort podcasts titles alphabetically", "Podcast title: A → Z");
|
||||
case SortType::TitleDescending:
|
||||
return i18nc("@label:chooser Sort podcasts titles in reverse alphabetical order", "Podcast title: Z → A");
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QString FeedsProxyModel::getSortIconName(SortType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case SortType::UnreadDescending:
|
||||
case SortType::NewDescending:
|
||||
case SortType::FavoriteDescending:
|
||||
return QStringLiteral("view-sort-descending");
|
||||
case SortType::UnreadAscending:
|
||||
case SortType::NewAscending:
|
||||
case SortType::FavoriteAscending:
|
||||
return QStringLiteral("view-sort-ascending");
|
||||
case SortType::TitleDescending:
|
||||
return QStringLiteral("view-sort-descending-name");
|
||||
case SortType::TitleAscending:
|
||||
return QStringLiteral("view-sort-ascending-name");
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QModelIndex>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QString>
|
||||
|
||||
#include "models/feedsmodel.h"
|
||||
|
||||
|
@ -18,12 +20,46 @@ class FeedsProxyModel : public QSortFilterProxyModel
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum SortType {
|
||||
UnreadDescending,
|
||||
UnreadAscending,
|
||||
NewDescending,
|
||||
NewAscending,
|
||||
FavoriteDescending,
|
||||
FavoriteAscending,
|
||||
TitleAscending,
|
||||
TitleDescending,
|
||||
};
|
||||
Q_ENUM(SortType)
|
||||
|
||||
Q_PROPERTY(QString searchFilter READ searchFilter WRITE setSearchFilter NOTIFY searchFilterChanged)
|
||||
Q_PROPERTY(SortType sortType READ sortType WRITE setSortType NOTIFY sortTypeChanged)
|
||||
|
||||
explicit FeedsProxyModel(QObject *parent = nullptr);
|
||||
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
QString searchFilter() const;
|
||||
SortType sortType() const;
|
||||
|
||||
void setSearchFilter(const QString &searchString);
|
||||
void setSortType(SortType type);
|
||||
|
||||
Q_INVOKABLE QString getSortName(SortType type) const;
|
||||
Q_INVOKABLE QString getSortIconName(SortType type) const;
|
||||
|
||||
Q_INVOKABLE QItemSelection createSelection(int rowa, int rowb);
|
||||
|
||||
Q_SIGNALS:
|
||||
void searchFilterChanged();
|
||||
void sortTypeChanged();
|
||||
|
||||
private:
|
||||
FeedsModel *m_feedsModel;
|
||||
|
||||
QString m_searchFilter;
|
||||
SortType m_currentSort = SortType::UnreadDescending;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FeedsProxyModel::SortType)
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.kde.kasts 1.0
|
|||
Controls.ItemDelegate {
|
||||
id: feedDelegate
|
||||
|
||||
property var countProperty: (kastsMainWindow.feedSorting === FeedsProxyModel.UnreadDescending || kastsMainWindow.feedSorting === FeedsProxyModel.UnreadAscending) ? feed.unreadEntryCount : ((kastsMainWindow.feedSorting === FeedsProxyModel.NewDescending || kastsMainWindow.feedSorting === FeedsProxyModel.NewAscending) ? feed.newEntryCount : ((kastsMainWindow.feedSorting === FeedsProxyModel.FavoriteDescending || kastsMainWindow.feedSorting === FeedsProxyModel.FavoriteAscending) ? feed.favoriteEntryCount : 0))
|
||||
property int cardSize: 0
|
||||
property int cardMargin: 0
|
||||
property int borderWidth: 1
|
||||
|
@ -170,7 +171,7 @@ Controls.ItemDelegate {
|
|||
|
||||
Rectangle {
|
||||
id: countRectangle
|
||||
visible: feed.unreadEntryCount > 0
|
||||
visible: countProperty > 0
|
||||
anchors.top: img.top
|
||||
anchors.right: img.right
|
||||
width: actionsButton.width
|
||||
|
@ -181,10 +182,10 @@ Controls.ItemDelegate {
|
|||
|
||||
Controls.Label {
|
||||
id: countLabel
|
||||
visible: feed.unreadEntryCount > 0
|
||||
visible: countProperty > 0
|
||||
anchors.centerIn: countRectangle
|
||||
anchors.margins: Kirigami.Units.smallSpacing
|
||||
text: feed.unreadEntryCount
|
||||
text: countProperty
|
||||
font.bold: true
|
||||
color: Kirigami.Theme.highlightedTextColor
|
||||
}
|
||||
|
|
|
@ -57,6 +57,62 @@ Kirigami.ScrollablePage {
|
|||
addSheet.open()
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
id: sortActionRoot
|
||||
icon.name: "view-sort"
|
||||
text: i18nc("@action:intoolbar Open menu with options to sort subscriptions", "Sort")
|
||||
|
||||
tooltip: i18nc("@info:tooltip", "Select how to sort subscriptions")
|
||||
|
||||
property Controls.ActionGroup sortGroup: Controls.ActionGroup { }
|
||||
|
||||
property Instantiator repeater: Instantiator {
|
||||
model: ListModel {
|
||||
id: sortModel
|
||||
// have to use script because i18n doesn't work within ListElement
|
||||
Component.onCompleted: {
|
||||
if (sortActionRoot.visible) {
|
||||
var sortList = [FeedsProxyModel.UnreadDescending,
|
||||
FeedsProxyModel.UnreadAscending,
|
||||
FeedsProxyModel.NewDescending,
|
||||
FeedsProxyModel.NewAscending,
|
||||
FeedsProxyModel.FavoriteDescending,
|
||||
FeedsProxyModel.FavoriteAscending,
|
||||
FeedsProxyModel.TitleAscending,
|
||||
FeedsProxyModel.TitleDescending]
|
||||
for (var i in sortList) {
|
||||
sortModel.append({"name": feedsModel.getSortName(sortList[i]),
|
||||
"iconName": feedsModel.getSortIconName(sortList[i]),
|
||||
"sortType": sortList[i]});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Action {
|
||||
visible: sortActionRoot.visible
|
||||
icon.name: model.iconName
|
||||
text: model.name
|
||||
checkable: true
|
||||
checked: kastsMainWindow.feedSorting === model.sortType
|
||||
Controls.ActionGroup.group: sortActionRoot.sortGroup
|
||||
|
||||
onTriggered: {
|
||||
kastsMainWindow.feedSorting = model.sortType;
|
||||
}
|
||||
}
|
||||
|
||||
onObjectAdded: (index, object) => {
|
||||
sortActionRoot.children.push(object);
|
||||
}
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
id: searchActionButton
|
||||
icon.name: "search"
|
||||
text: i18nc("@action:intoolbar", "Search")
|
||||
checkable: true
|
||||
},
|
||||
Kirigami.Action {
|
||||
id: importAction
|
||||
text: i18nc("@action:intoolbar", "Import Podcasts…")
|
||||
|
@ -83,6 +139,19 @@ Kirigami.ScrollablePage {
|
|||
// TODO: KF6 replace contextualActions with actions
|
||||
contextualActions: pageActions
|
||||
|
||||
header: Loader {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
active: searchActionButton.checked
|
||||
visible: active
|
||||
sourceComponent: SearchBar {
|
||||
proxyModel: feedsModel
|
||||
parentKey: searchActionButton
|
||||
showSearchFilters: false
|
||||
}
|
||||
}
|
||||
|
||||
AddFeedSheet {
|
||||
id: addSheet
|
||||
}
|
||||
|
@ -114,9 +183,9 @@ Kirigami.ScrollablePage {
|
|||
visible: feedList.count === 0
|
||||
width: Kirigami.Units.gridUnit * 20
|
||||
anchors.centerIn: parent
|
||||
type: Kirigami.PlaceholderMessage.Actionable
|
||||
text: i18nc("@info Placeholder message for empty podcast list", "No podcasts added yet")
|
||||
explanation: i18nc("@info:tipoftheday", "Get started by adding podcasts:")
|
||||
type: feedsModel.searchFilter === "" ? Kirigami.PlaceholderMessage.Actionable : Kirigami.PlaceholderMessage.Informational
|
||||
text: feedsModel.searchFilter === "" ? i18nc("@info Placeholder message for empty podcast list", "No podcasts added yet") : i18nc("@info Placeholder message for podcast list when no podcast matches the search criteria", "No podcasts found")
|
||||
explanation: feedsModel.searchFilter === "" ? i18nc("@info:tipoftheday", "Get started by adding podcasts:") : null
|
||||
|
||||
readonly property int buttonSize: Math.max(discoverButton.implicitWidth, addButton.implicitWidth, importButton.implicitWidth, syncButton.implicitWidth)
|
||||
|
||||
|
@ -124,6 +193,7 @@ Kirigami.ScrollablePage {
|
|||
// to give them more descriptive names
|
||||
Controls.Button {
|
||||
id: discoverButton
|
||||
visible: feedsModel.searchFilter === ""
|
||||
Layout.preferredWidth: placeholderMessage.buttonSize
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Kirigami.Units.gridUnit
|
||||
|
@ -136,6 +206,7 @@ Kirigami.ScrollablePage {
|
|||
|
||||
Controls.Button {
|
||||
id: addButton
|
||||
visible: feedsModel.searchFilter === ""
|
||||
Layout.preferredWidth: placeholderMessage.buttonSize
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
action: addAction
|
||||
|
@ -143,6 +214,7 @@ Kirigami.ScrollablePage {
|
|||
|
||||
Controls.Button {
|
||||
id: importButton
|
||||
visible: feedsModel.searchFilter === ""
|
||||
Layout.preferredWidth: placeholderMessage.buttonSize
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
action: importAction
|
||||
|
@ -150,6 +222,7 @@ Kirigami.ScrollablePage {
|
|||
|
||||
Controls.Button {
|
||||
id: syncButton
|
||||
visible: feedsModel.searchFilter === ""
|
||||
Layout.preferredWidth: placeholderMessage.buttonSize
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
action: Kirigami.Action {
|
||||
|
@ -184,6 +257,7 @@ Kirigami.ScrollablePage {
|
|||
|
||||
model: FeedsProxyModel {
|
||||
id: feedsModel
|
||||
sortType: kastsMainWindow.feedSorting
|
||||
}
|
||||
|
||||
delegate: FeedListDelegate {
|
||||
|
@ -207,9 +281,9 @@ Kirigami.ScrollablePage {
|
|||
Connections {
|
||||
target: feedList.model
|
||||
function onModelAboutToBeReset() {
|
||||
selectionForContextMenu = [];
|
||||
feedList.selectionForContextMenu = [];
|
||||
feedList.selectionModel.clear();
|
||||
feedList.selectionModel.setCurrentIndex(model.index(0, 0), ItemSelectionModel.Current); // Only set current item; don't select it
|
||||
feedList.selectionModel.setCurrentIndex(feedList.model.index(0, 0), ItemSelectionModel.Current); // Only set current item; don't select it
|
||||
currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ Controls.Control {
|
|||
|
||||
required property var proxyModel
|
||||
required property var parentKey
|
||||
property bool showSearchFilters: true
|
||||
|
||||
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
|
@ -46,6 +47,8 @@ Controls.Control {
|
|||
|
||||
Kirigami.Action {
|
||||
id: searchSettingsButton
|
||||
visible: showSearchFilters
|
||||
enabled: visible
|
||||
icon.name: "settings-configure"
|
||||
text: i18nc("@action:intoolbar", "Advanced Search Options")
|
||||
|
||||
|
@ -68,7 +71,6 @@ Controls.Control {
|
|||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
proxyModel.filterType = AbstractEpisodeProxyModel.NoFilter;
|
||||
proxyModel.searchFilter = "";
|
||||
parentKey.checked = false;
|
||||
event.accepted = true;
|
||||
|
@ -88,6 +90,7 @@ Controls.Control {
|
|||
|
||||
function reload() {
|
||||
clear();
|
||||
if (showSearchFilters) {
|
||||
var searchList = [AbstractEpisodeProxyModel.TitleFlag,
|
||||
AbstractEpisodeProxyModel.ContentFlag,
|
||||
AbstractEpisodeProxyModel.FeedNameFlag]
|
||||
|
@ -97,6 +100,7 @@ Controls.Control {
|
|||
"checked": proxyModel.searchFlags & searchList[i]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
reload();
|
||||
|
|
|
@ -43,6 +43,7 @@ Kirigami.ApplicationWindow {
|
|||
}
|
||||
property var lastFeed: ""
|
||||
property string currentPage: ""
|
||||
property int feedSorting: FeedsProxyModel.UnreadDescending
|
||||
|
||||
property bool isWidescreen: kastsMainWindow.width > kastsMainWindow.height
|
||||
|
||||
|
@ -85,6 +86,7 @@ Kirigami.ApplicationWindow {
|
|||
property var desktopHeight
|
||||
property int headerSize: Kirigami.Units.gridUnit * 5
|
||||
property alias lastOpenedPage: kastsMainWindow.currentPage
|
||||
property alias feedSorting: kastsMainWindow.feedSorting
|
||||
}
|
||||
|
||||
function saveWindowLayout() {
|
||||
|
|
Loading…
Reference in New Issue