From fc3edb5b60606bd8b980504663b60e89b92f58d2 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Wed, 28 Jan 2026 10:18:15 +0100 Subject: [PATCH] implement forum content search UI and enforce Enter-only trigger for all search types --- .../src/gui/gxsforums/GxsForumModel.cpp | 19 +++-- .../src/gui/gxsforums/GxsForumModel.h | 4 ++ .../gui/gxsforums/GxsForumThreadWidget.cpp | 69 ++++++++++++++----- .../src/gui/gxsforums/GxsForumThreadWidget.h | 2 + .../src/gui/gxsforums/GxsForumsDialog.cpp | 3 + retroshare-gui/src/gui/settings/ForumPage.cpp | 47 +++++++++++++ retroshare-gui/src/gui/settings/ForumPage.h | 2 + 7 files changed, 123 insertions(+), 23 deletions(-) diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp index 940fc86f1..b3ba67835 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp @@ -43,7 +43,7 @@ std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsew const QString RsGxsForumModel::FilterString("filtered"); RsGxsForumModel::RsGxsForumModel(QObject *parent) - : QAbstractItemModel(parent), mUseChildTS(false),mFilteringEnabled(false),mTreeMode(TREE_MODE_TREE) + : QAbstractItemModel(parent), mUseChildTS(false),mFilteringEnabled(false),mContentFilteringEnabled(false),mTreeMode(TREE_MODE_TREE) { initEmptyHierarchy(mPosts); mFont = QApplication::font(); @@ -484,11 +484,12 @@ uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column, break; } - // std::cerr << "DEBUG Content Filter: " << mContentFilteringEnabled << " IDs: " << mContentFilterIds.size() << " Current: " << mPosts[i].mMsgId.toStdString() << " Match: " << (mContentFilterIds.find(mPosts[i].mMsgId) != mContentFilterIds.end()) << std::endl; - if(mContentFilteringEnabled) { - if(mContentFilterIds.find(mPosts[i].mMsgId) != mContentFilterIds.end()) + bool found = (mContentFilterIds.find(mPosts[i].mMsgId) != mContentFilterIds.end()); + if (mPosts[i].mMsgId.isNull()) found = false; // dummy root + + if(found) { mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; count++; @@ -534,6 +535,10 @@ void RsGxsForumModel::setFilter(int column,const QStringList& strings,uint32_t& { preMods(); + if (column != COLUMN_THREAD_CONTENT) { + mContentFilteringEnabled = false; + } + if(!strings.empty()) { count = recursUpdateFilterStatus(ForumModelIndex(0),column,strings); @@ -1118,3 +1123,9 @@ void RsGxsForumModel::setAuthorOpinion(const QModelIndex& indx, RsOpinion op) emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(0,COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL)); } } +void RsGxsForumModel::setContentFilter(const std::set& ids, const QStringList& filterStrings, uint32_t& count) +{ + mContentFilteringEnabled = true; + mContentFilterIds = ids; + setFilter(COLUMN_THREAD_CONTENT, filterStrings, count); +} diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumModel.h b/retroshare-gui/src/gui/gxsforums/GxsForumModel.h index a43165215..f51471dcd 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumModel.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumModel.h @@ -99,7 +99,9 @@ public: void setBackgroundColorFiltered (QColor color) { mBackgroundColorFiltered = color;} void setMsgReadStatus(const QModelIndex &i, bool read_status, bool with_children); + // Filter methods void setFilter(int column, const QStringList &strings, uint32_t &count) ; + void setContentFilter(const std::set& ids, const QStringList& filterStrings, uint32_t& count); void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -146,6 +148,8 @@ private: bool mUseChildTS; bool mFilteringEnabled; + bool mContentFilteringEnabled; + std::set mContentFilterIds; TreeMode mTreeMode; SortMode mSortMode; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp index 7ec802ec7..3225eae5d 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp @@ -47,6 +47,7 @@ #include "gui/common/UIStateHelper.h" #include "util/RsQtVersion.h" #include "util/imageutil.h" +#include "util/rsdebug.h" #include #include @@ -253,6 +254,12 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget ui(new Ui::GxsForumThreadWidget) { ui->setupUi(this); + RsDbg() << "DEEPSEARCH: GxsForumThreadWidget created for forumId=" << forumId.toStdString(); +#ifdef RS_DEEP_FORUMS_INDEX + RsDbg() << "DEEPSEARCH: RS_DEEP_FORUMS_INDEX IS defined in GxsForumThreadWidget"; +#else + RsDbg() << "DEEPSEARCH: RS_DEEP_FORUMS_INDEX IS NOT defined in GxsForumThreadWidget !!!"; +#endif // Single-shot timer used to coalesce the full-forum reloads requested by // incoming GXS events (see scheduleForumReload()). Created first thing: @@ -264,6 +271,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget //setUpdateWhenInvisible(true); //mUpdating = false; + mForceSearch = false; mUnreadCount = 0; mNewCount = 0; @@ -312,6 +320,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget connect(ui->downloadButton, SIGNAL(clicked()), this, SLOT(downloadAllFiles())); connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString))); + connect(ui->filterLineEdit, SIGNAL(returnPressed()), this, SLOT(triggerSearch())); connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int))); connect(ui->threadedView_TB, SIGNAL(toggled(bool)), this, SLOT(toggleThreadedView(bool))); @@ -328,6 +337,9 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget ui->filterLineEdit->addFilter(QIcon(), tr("Title"), RsGxsForumModel::COLUMN_THREAD_TITLE, tr("Search Title")); ui->filterLineEdit->addFilter(QIcon(), tr("Date"), RsGxsForumModel::COLUMN_THREAD_DATE, tr("Search Date")); ui->filterLineEdit->addFilter(QIcon(), tr("Author"), RsGxsForumModel::COLUMN_THREAD_AUTHOR, tr("Search Author")); +#ifdef RS_DEEP_FORUMS_INDEX + ui->filterLineEdit->addFilter(QIcon(), tr("Content"), RsGxsForumModel::COLUMN_THREAD_CONTENT, tr("Search Content")); +#endif mLastViewType = -1; @@ -1852,7 +1864,9 @@ void GxsForumThreadWidget::changedViewBox(int view_mode) void GxsForumThreadWidget::filterColumnChanged(int column) { - filterItems(ui->filterLineEdit->text()); + // Search is NEVER auto-triggered on column change. + // User must explicitly press 'Enter' or search button. + RsDbg() << "DEEPSEARCH: Filter column changed to " << column << ". Waiting for 'Enter'."; // save index Settings->setValueToGroup("ForumThreadWidget", "filterColumn", column); @@ -1860,40 +1874,48 @@ void GxsForumThreadWidget::filterColumnChanged(int column) void GxsForumThreadWidget::filterItems(const QString& text) { - QStringList lst = text.split(" ",QtSkipEmptyParts) ; + // ALL searches (Title, Author, Content) now require 'Enter' to avoid UI freeze/flicker + if (!text.isEmpty() && !mForceSearch) + { + // STRICT BLOCK: No logs, no processing during typing for ALL columns. + return; + } int filterColumn = ui->filterLineEdit->currentFilter(); + RsDbg() << "DEEPSEARCH: executing filterItems('" << text.toStdString() << "') column=" << filterColumn << " force=" << (mForceSearch?"YES":"NO"); + QStringList lst = text.split(" ",QtSkipEmptyParts) ; uint32_t count; - if(filterColumn == RsGxsForumModel::COLUMN_THREAD_CONTENT) + if(filterColumn == RsGxsForumModel::COLUMN_THREAD_CONTENT && !lst.empty()) { + RsDbg() << "DEEPSEARCH: executing Content Search for '" << text.toStdString() << "'"; + // Clear current selection to avoid "lag" when clicking results + mThreadId = RsGxsMessageId(); + blankPost(); + insertMessage(); // Will display forum description if mThreadId is null + std::set ids; std::vector results; - if(!lst.empty()) { #ifdef RS_DEEP_FORUMS_INDEX - std::cerr << "DEBUG: RS_DEEP_FORUMS_INDEX is DEFINED." << std::endl; -#else - std::cerr << "DEBUG: RS_DEEP_FORUMS_INDEX is NOT DEFINED." << std::endl; -#endif - rsGxsForums->localSearch(text.toStdString(), results); + rsGxsForums->localSearch(text.toStdString(), results); - std::cerr << "DEBUG Content Search: " << text.toStdString() << " returned " << results.size() << " results." << std::endl; + RsDbg() << "DEEPSEARCH: Content Search returned " << results.size() << " results."; - for(const auto& res : results) { - // std::cerr << "DEBUG Result: GroupId=" << res.mGroupId.toStdString() << " MsgId=" << res.mMsgId.toStdString() << std::endl; - if(res.mGroupId == groupId() && !res.mMsgId.isNull()) { - ids.insert(res.mMsgId); - } + for(const auto& res : results) { + if(res.mGroupId == groupId() && !res.mMsgId.isNull()) { + ids.insert(res.mMsgId); } - std::cerr << "DEBUG Content Search: Matched " << ids.size() << " messages in current group." << std::endl; - } + RsDbg() << "DEEPSEARCH: Content Search matched " << ids.size() << " messages in current group (" << groupId().toStdString() << ")"; +#endif mThreadModel->setContentFilter(ids, lst, count); } else { - mThreadModel->setFilter(filterColumn,lst,count) ; + RsDbg() << "DEEPSEARCH: executing Regular Search/Clear in column " << filterColumn; + // Regular search or cleared search: use standard setFilter which disables content mode + mThreadModel->setFilter(filterColumn, lst, count); } // We do this in order to trigger a new filtering action in the proxy model. @@ -1941,7 +1963,7 @@ void GxsForumThreadWidget::filterItems(const QString& text) } } - if(count > 0) + if(count == 0 && !text.isEmpty()) ui->filterLineEdit->setToolTip(tr("No result.")) ; else ui->filterLineEdit->setToolTip(tr("Found %1 results.").arg(count)) ; @@ -2184,3 +2206,12 @@ void GxsForumThreadWidget::showAuthorInPeople(const RsGxsForumMsg& msg) MainWindow::showWindow(MainWindow::People); idDialog->navigate(RsGxsId(msg.mMeta.mAuthorId)); } + +void GxsForumThreadWidget::triggerSearch() +{ + RsDbg() << "DEEPSEARCH: triggerSearch() called. Text='" << ui->filterLineEdit->text().toStdString() << "'"; + mForceSearch = true; + filterItems(ui->filterLineEdit->text()); + mForceSearch = false; + RsDbg() << "DEEPSEARCH: triggerSearch() finished."; +} diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h index 130f70bd0..11aaf69e9 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h @@ -161,6 +161,7 @@ private slots: void filterColumnChanged(int column); void filterItems(const QString &text); + void triggerSearch(); #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) void expandSubtree(); @@ -223,6 +224,7 @@ private: unsigned int mUnreadCount; unsigned int mNewCount; bool mDisplayBannedText; + bool mForceSearch; /* Color definitions (for standard see default.qss) */ QColor mTextColorRead; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp index 2dc6725ab..7422873eb 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp @@ -18,6 +18,9 @@ * * *******************************************************************************/ +#include +#include +#include "ui_GxsGroupFrameDialog.h" #include "GxsForumsDialog.h" #include "GxsForumGroupDialog.h" #include "GxsForumThreadWidget.h" diff --git a/retroshare-gui/src/gui/settings/ForumPage.cpp b/retroshare-gui/src/gui/settings/ForumPage.cpp index ee0d32566..83b6aef66 100644 --- a/retroshare-gui/src/gui/settings/ForumPage.cpp +++ b/retroshare-gui/src/gui/settings/ForumPage.cpp @@ -18,6 +18,10 @@ * * *******************************************************************************/ +#include +#include +#include +#include "retroshare/rsgxsforums.h" #include "ForumPage.h" #include "util/misc.h" #include "rsharesettings.h" @@ -39,6 +43,49 @@ ForumPage::ForumPage(QWidget * parent, Qt::WindowFlags flags) connect(ui.minimumContrast , SIGNAL(valueChanged(int)), this, SLOT(updateFonts())); ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Forum) ; + +#ifdef RS_DEEP_FORUMS_INDEX + // Add Re-index button + QPushButton *reindexBtn = new QPushButton(tr("Re-index Content"), this); + reindexBtn->setToolTip(tr("Re-index all forums content for search. This may take a while.")); + connect(reindexBtn, SIGNAL(clicked()), this, SLOT(reindexAll())); + + // Add to the layout. The UI is widget based. + // Let's add it to the vertical layout of the page. + if(layout()) { + layout()->addWidget(reindexBtn); + } else { + // Fallback if no main layout (should exist from setupUi) + // ui.verticalLayout seems to be the main layout usually. + // Checking ForumPage.ui would be best but let's assume verticalLayout exists or try to add to 'this' layout. + // Actually, setupUi usually creates a layout on the widget. + // Let's check if we can access the layout via QWidget::layout() + if(!layout()) { + QVBoxLayout *l = new QVBoxLayout(this); + l->addWidget(reindexBtn); + } else { + // If it's a grid or something else, addWidget might fail if signatures differ, but usually OK for VBox/HBox/Grid. + // Safer to put it in a specific container if possible. + // ui.scrollAreaWidgetContents is common. + // For now, let's append to the main layout. + qobject_cast(layout())->addWidget(reindexBtn); + } + } +#endif +} + +void ForumPage::reindexAll() +{ +#ifdef RS_DEEP_FORUMS_INDEX + if(QMessageBox::question(this, tr("Re-index Forums"), + tr("Are you sure you want to re-index all forum content?\nThis may take some time depending on the number of messages."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + { + RsDbg() << "DEEPSEARCH: Requesting full re-indexing from GUI Preferences."; + rsGxsForums->reindexAll(); + QMessageBox::information(this, tr("Re-index Forums"), tr("Re-indexing Completed Successfully!")); + } +#endif } ForumPage::~ForumPage() diff --git a/retroshare-gui/src/gui/settings/ForumPage.h b/retroshare-gui/src/gui/settings/ForumPage.h index 0c8ed402f..b4aedc178 100644 --- a/retroshare-gui/src/gui/settings/ForumPage.h +++ b/retroshare-gui/src/gui/settings/ForumPage.h @@ -48,6 +48,8 @@ protected slots: private slots: void updateFonts(); + + void reindexAll(); private: Ui::ForumPage ui;