diff --git a/src/gui/rss/articlelistwidget.cpp b/src/gui/rss/articlelistwidget.cpp index 561af742f..a7ba1d80e 100644 --- a/src/gui/rss/articlelistwidget.cpp +++ b/src/gui/rss/articlelistwidget.cpp @@ -64,7 +64,7 @@ QListWidgetItem *ArticleListWidget::mapRSSArticle(RSS::Article *rssArticle) cons return m_rssArticleToListItemMapping.value(rssArticle); } -void ArticleListWidget::setRSSItem(RSS::Item *rssItem, bool unreadOnly) +void ArticleListWidget::setRSSItem(RSS::Item *rssItem, bool unreadOnly, const QString &filter) { // Clear the list first clear(); @@ -82,7 +82,7 @@ void ArticleListWidget::setRSSItem(RSS::Item *rssItem, bool unreadOnly) for (auto *article : asConst(rssItem->articles())) { - if (!(m_unreadOnly && article->isRead())) + if (!(m_unreadOnly && article->isRead()) && (filter.isEmpty() || article->title().contains(filter, Qt::CaseInsensitive))) { auto *item = createItem(article); addItem(item); diff --git a/src/gui/rss/articlelistwidget.h b/src/gui/rss/articlelistwidget.h index b576b9d66..9c42801ff 100644 --- a/src/gui/rss/articlelistwidget.h +++ b/src/gui/rss/articlelistwidget.h @@ -48,7 +48,7 @@ public: RSS::Article *getRSSArticle(QListWidgetItem *item) const; QListWidgetItem *mapRSSArticle(RSS::Article *rssArticle) const; - void setRSSItem(RSS::Item *rssItem, bool unreadOnly = false); + void setRSSItem(RSS::Item *rssItem, bool unreadOnly, const QString &filter); private slots: void handleArticleAdded(RSS::Article *rssArticle); diff --git a/src/gui/rss/rsswidget.cpp b/src/gui/rss/rsswidget.cpp index fb711b0ac..5198b9fa6 100644 --- a/src/gui/rss/rsswidget.cpp +++ b/src/gui/rss/rsswidget.cpp @@ -49,6 +49,7 @@ #include "base/rss/rss_session.h" #include "gui/autoexpandabledialog.h" #include "gui/interfaces/iguiapplication.h" +#include "gui/lineedit.h" #include "gui/uithememanager.h" #include "gui/utils/keysequence.h" #include "articlelistwidget.h" @@ -108,6 +109,7 @@ namespace RSSWidget::RSSWidget(IGUIApplication *app, QWidget *parent) : GUIApplicationComponent(app, parent) , m_ui {new Ui::RSSWidget} + , m_rssFilter {new LineEdit(this)} { m_ui->setupUi(this); @@ -130,6 +132,13 @@ RSSWidget::RSSWidget(IGUIApplication *app, QWidget *parent) m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_s, u"download"_s)); #endif + m_rssFilter->setMaximumWidth(200); + m_rssFilter->setPlaceholderText(tr("Filter feed items...")); + + const int spacerIndex = m_ui->horizontalLayout->indexOf(m_ui->spacer1); + m_ui->horizontalLayout->insertWidget((spacerIndex + 1), m_rssFilter); + + connect(m_rssFilter, &QLineEdit::textChanged, this, &RSSWidget::handleRSSFilterTextChanged); connect(m_ui->articleListWidget, &ArticleListWidget::customContextMenuRequested, this, &RSSWidget::displayItemsListMenu); connect(m_ui->articleListWidget, &ArticleListWidget::currentItemChanged, this, &RSSWidget::handleCurrentArticleItemChanged); connect(m_ui->articleListWidget, &ArticleListWidget::itemDoubleClicked, this, &RSSWidget::downloadSelectedTorrents); @@ -568,7 +577,8 @@ void RSSWidget::copySelectedFeedsURL() void RSSWidget::handleCurrentFeedItemChanged(QTreeWidgetItem *currentItem) { m_ui->articleListWidget->setRSSItem(m_ui->feedListWidget->getRSSItem(currentItem) - , (currentItem == m_ui->feedListWidget->stickyUnreadItem())); + , (currentItem == m_ui->feedListWidget->stickyUnreadItem()) + , m_rssFilter->text()); } void RSSWidget::on_markReadButton_clicked() @@ -641,6 +651,14 @@ void RSSWidget::handleUnreadCountChanged() emit unreadCountUpdated(RSS::Session::instance()->rootFolder()->unreadCount()); } +void RSSWidget::handleRSSFilterTextChanged(const QString &newFilter) +{ + QTreeWidgetItem *currentItem = m_ui->feedListWidget->currentItem(); + m_ui->articleListWidget->setRSSItem(m_ui->feedListWidget->getRSSItem(currentItem) + , (currentItem == m_ui->feedListWidget->stickyUnreadItem()) + , newFilter); +} + bool RSSWidget::eventFilter(QObject *obj, QEvent *event) { if ((obj == m_ui->textBrowser) && (event->type() == QEvent::PaletteChange)) diff --git a/src/gui/rss/rsswidget.h b/src/gui/rss/rsswidget.h index 67d1e474c..1d6951d5c 100644 --- a/src/gui/rss/rsswidget.h +++ b/src/gui/rss/rsswidget.h @@ -34,6 +34,7 @@ #include "gui/guiapplicationcomponent.h" +class LineEdit; class QListWidgetItem; class QTreeWidgetItem; @@ -85,10 +86,12 @@ private slots: void on_rssDownloaderBtn_clicked(); void handleSessionProcessingStateChanged(bool enabled); void handleUnreadCountChanged(); + void handleRSSFilterTextChanged(const QString &newFilter); private: bool eventFilter(QObject *obj, QEvent *event) override; void renderArticle(const RSS::Article *article) const; Ui::RSSWidget *m_ui = nullptr; + LineEdit *m_rssFilter = nullptr; }; diff --git a/src/webui/www/private/views/rss.html b/src/webui/www/private/views/rss.html index f2e790878..e6ea67be7 100644 --- a/src/webui/www/private/views/rss.html +++ b/src/webui/www/private/views/rss.html @@ -89,10 +89,32 @@ padding: 3px 6px; } + #rssButtonBar input { + background-color: var(--color-background-default); + background-image: url("../images/edit-find.svg"); + background-position: 2px; + background-repeat: no-repeat; + background-size: 1.5em; + border: 1px solid var(--color-border-default); + border-radius: 3px; + min-width: 170px; + padding: 2px 2px 2px 25px; + } + + #rssButtonBar div { + display: inline-block; + vertical-align: top; + } + #rssButtonBar button img { margin: 0 5px -3px 0; } + #rssFilterToolbar { + float: right; + margin-right: 5px; + } + #rssContentView table { width: 100%; } @@ -120,9 +142,12 @@ - +
+ + +
@@ -277,6 +302,17 @@ rssFeedContextMenu.updateMenuItems(); } }); + document.getElementById("rssFilterInput").addEventListener("input", (event) => { + const rowId = rssFeedTable.selectedRows[0]; + let path = ""; + for (const row of rssFeedTable.getRowValues()) { + if (row.rowId === rowId) { + path = row.full_data.dataPath; + break; + } + } + qBittorrent.Rss.showRssFeed(path); + }); document.getElementById("CopyFeedURL").addEventListener("click", async (event) => { let joined = ""; @@ -406,6 +442,12 @@ if (path === "") visibleArticles = visibleArticles.filter((a) => !a.isRead); + const rssFilterInput = document.getElementById("rssFilterInput"); + if (rssFilterInput.value.length > 0) { + const lowerFilter = rssFilterInput.value.toLowerCase(); + visibleArticles = visibleArticles.filter((a) => a.title.toLowerCase().includes(lowerFilter)); + } + let rowID = -1; visibleArticles.sort((e1, e2) => new Date(e2.date) - new Date(e1.date)) .each((torrentEntry) => {