From 37f4ddc3d09638c64b7afd0b020e64b4143cbfc3 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Mon, 26 Jan 2026 21:07:46 +0100 Subject: [PATCH] Optimized Shared Files view performance by caching file details to eliminate UI freezes and increasing the update frequency for faster list population --- .../gui/FileTransfer/SharedFilesDialog.cpp | 32 ++++++- retroshare-gui/src/gui/RemoteDirModel.cpp | 96 +++++++++++++++++-- retroshare-gui/src/gui/RemoteDirModel.h | 17 +++- 3 files changed, 135 insertions(+), 10 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index fd30ede75..810820b60 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -145,7 +145,8 @@ public: void setUploadedOnly(bool val) { if (m_uploadedOnly != val) { m_uploadedOnly = val; - invalidateFilter(); + // invalidateFilter(); // CRASH FIX: Do not invalidate incrementally. + // The dialog calls model->update() which triggers a full Reset. } } @@ -556,7 +557,7 @@ void SharedFilesDialog::changeCurrentViewModel(int viewTypeIndex) // recursRestoreExpandedItems(ui.dirTreeView->rootIndex(),expanded_indexes); // Force re-application of filter on the new model - mLastFilterText.clear(); + mLastFilterText = "FORCE_UPDATE_ON_VIEW_SWITCH"; FilterItems(); // MODIFICATION: Expand tree if "Uploaded Only" is active, otherwise items remain hidden in collapsed folders. @@ -1284,6 +1285,10 @@ void SharedFilesDialog::FilterItems() if(text.length() < 3) { model->filterItems(std::list(), found) ; + + // MODIFICATION: Sync proxy filter to ensure it doesn't hold stale text + if (proxyModel) proxyModel->setFilterFixedString(QString()); + return ; } @@ -1294,6 +1299,20 @@ void SharedFilesDialog::FilterItems() model->filterItems(keywords, found) ; + // MODIFICATION: Hybrid filtering. + // Flat View uses model->filterItems (Manual population). + // Tree View relies on Proxy for text filtering (Hybrid). + // We must sync the proxy to ensure Tree View works and doesn't hold stale data. + if (proxyModel) { + if(ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { + proxyModel->setFilterFixedString(RETROSHARE_DIR_MODEL_FILTER_STRING); + } else { + // In Flat View, Model handles filtering logic (including multi-keyword). + // Proxy should NOT double-filter (which breaks multi-keyword). + proxyModel->setFilterFixedString(QString()); + } + } + // Note: model->filterItems() already calls update() (reset), // so proxy invalidation is redundant and handled automatically. if(found > 0) expandAll(); @@ -1659,7 +1678,14 @@ void SharedFilesDialog::filterUploadedOnlyToggled(bool checked) if(ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { if (checked) expandAll(); - else ui.dirTreeView->collapseAll(); + else { + // MODIFICATION: Do not collapse if we have a text filter active, otherwise results are hidden. + if (ui.filterPatternLineEdit->text().length() >= 3) { + expandAll(); + } else { + ui.dirTreeView->collapseAll(); + } + } } } diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index d0eafa170..56362814c 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -57,7 +57,6 @@ #define REMOTEDIRMODEL_COLUMN_WN_VISU_DIR 5 #define REMOTEDIRMODEL_COLUMN_UPLOADED 6 #define REMOTEDIRMODEL_COLUMN_COUNT 7 -#define RETROSHARE_DIR_MODEL_FILTER_STRING "filtered" static const uint32_t FLAT_VIEW_MAX_REFS_PER_SECOND = 10000 ; static const size_t FLAT_VIEW_MAX_REFS_TABLE_SIZE = 30000 ; // @@ -219,6 +218,7 @@ void TreeStyle_RDM::recalculateDirectoryTotals() } } } + } // MODIFICATION D: Check if a specific node or any of its descendants have uploads @@ -251,6 +251,7 @@ bool TreeStyle_RDM::hasUploads(void *ref) const void TreeStyle_RDM::update() { + // Recalculate totals before notifying view update recalculateDirectoryTotals(); @@ -587,8 +588,14 @@ void FlatStyle_RDM::update() // Previously, this was guarded by if(_needs_update), preventing filter updates. // if(_needs_update) { - // Prevent nested updates/resets which confuse the view - if(mUpdating) return; + if(mUpdating) return; + + + + { + RS_STACK_MUTEX(_ref_mutex); + m_cache.clear(); + } preMods() ; postMods() ; @@ -626,8 +633,36 @@ QString FlatStyle_RDM::computeDirectoryPath(const DirDetails& details) const return dir ; } +QVariant FlatStyle_RDM::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) + { + void *ref = index.internalPointer(); + + // Try Lock-less read first? No, use lock for safety matching updateRefs + RS_STACK_MUTEX(_ref_mutex); + if (m_cache.contains(ref)) + { + const CachedFileDetails &cfd = m_cache[ref]; + switch(index.column()) + { + case REMOTEDIRMODEL_COLUMN_NAME: return cfd.name; + case REMOTEDIRMODEL_COLUMN_SIZE: return cfd.sizeStr; + case REMOTEDIRMODEL_COLUMN_AGE: return cfd.ageStr; + case REMOTEDIRMODEL_COLUMN_UPLOADED: return cfd.uploadStr; + } + } + } + + return RetroshareDirModel::data(index, role); +} + QVariant FlatStyle_RDM::displayRole(const DirDetails& details,int coln) const { + if (details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) /* File */ switch(coln) { @@ -1508,6 +1543,10 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin if(result_list.empty()) { mFilteredPointers.clear(); + // MODIFICATION: Insert NULL to signal that we have an active filter which returned no results. + // updateRefs will see mFilteredPointers is not empty, enter the filtered path, encounter NULL, skip it, + // and resulting view will be empty (correct behavior) instead of showing all files. + mFilteredPointers.insert(NULL); update(); return ; } @@ -1800,6 +1839,7 @@ void FlatStyle_RDM::updateRefs() uint32_t nb_treated_refs = 0 ; + { RS_STACK_MUTEX(_ref_mutex) ; @@ -1818,9 +1858,29 @@ void FlatStyle_RDM::updateRefs() if(requestDirDetails(ref, RemoteMode, details)) { if(details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) + { _ref_entries.push_back(ref); + + CachedFileDetails cfd; + cfd.name = QString::fromUtf8(details.name.c_str()); + cfd.sizeStr = misc::friendlyUnit(details.size); + + if(details.type == DIR_TYPE_FILE) + cfd.ageStr = misc::timeRelativeToNow(details.max_mtime); + else { + FileInfo fi; + if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA , fi)) + cfd.ageStr = misc::timeRelativeToNow((rstime_t)fi.age-(30 * 3600 * 24)); + } + + uint64_t x = rsFiles->getCumulativeUpload(details.hash); + cfd.uploadStr = x ? misc::friendlyUnit(x) : QString(); + + m_cache[ref] = cfd; + } } } + _needs_update = false; // We are done } else @@ -1839,7 +1899,26 @@ void FlatStyle_RDM::updateRefs() if (requestDirDetails(ref, RemoteMode,details)) { if(details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) // only push files, not directories nor persons. + { _ref_entries.push_back(ref) ; + + CachedFileDetails cfd; + cfd.name = QString::fromUtf8(details.name.c_str()); + cfd.sizeStr = misc::friendlyUnit(details.size); + + if(details.type == DIR_TYPE_FILE) + cfd.ageStr = misc::timeRelativeToNow(details.max_mtime); + else { + FileInfo fi; + if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA , fi)) + cfd.ageStr = misc::timeRelativeToNow((rstime_t)fi.age-(30 * 3600 * 24)); + } + + uint64_t x = rsFiles->getCumulativeUpload(details.hash); + cfd.uploadStr = x ? misc::friendlyUnit(x) : QString(); + + m_cache[ref] = cfd; + } #ifdef RDM_DEBUG std::cerr << "FlatStyle_RDM::postMods(): adding ref " << ref << std::endl; #endif @@ -1854,8 +1933,9 @@ void FlatStyle_RDM::updateRefs() if(++nb_treated_refs > FLAT_VIEW_MAX_REFS_PER_SECOND) // we've done enough, let's give back hand to { // the user and setup a timer to finish the job later. + if(visible()) - QTimer::singleShot(2000,this,SLOT(updateRefs())) ; + QTimer::singleShot(10,this,SLOT(updateRefs())) ; // Reduced from 2000ms to 10ms for faster loading else std::cerr << "Not visible: suspending update"<< std::endl; break ; @@ -1865,8 +1945,12 @@ void FlatStyle_RDM::updateRefs() std::cerr << "reference tab contains " << std::dec << _ref_entries.size() << " files" << std::endl; } - if(_ref_stack.empty()) - _needs_update = false ; + // The loop is finished. + if(_ref_stack.empty()) { + + _needs_update = false ; + } + RetroshareDirModel::postMods() ; } diff --git a/retroshare-gui/src/gui/RemoteDirModel.h b/retroshare-gui/src/gui/RemoteDirModel.h index a9aa630a4..286fd6f2a 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.h +++ b/retroshare-gui/src/gui/RemoteDirModel.h @@ -21,6 +21,8 @@ #ifndef REMOTE_DIR_MODEL #define REMOTE_DIR_MODEL +#define RETROSHARE_DIR_MODEL_FILTER_STRING "filtered" + #include #include @@ -255,7 +257,11 @@ class FlatStyle_RDM: public RetroshareDirModel virtual void updateRef(const QModelIndex&) const {} // MODIFICATION H: Implement hasUploads for Flat Style to fix compilation virtual bool hasUploads(void *ref) const; - virtual QVariant displayRole(const DirDetails&,int) const ; + + // MODIFICATION: Override data() to use internal cache for Flat View + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + virtual QVariant displayRole(const DirDetails&,int) const ; virtual QVariant sortRole(const QModelIndex&,const DirDetails&,int) const ; //Overloaded from QAbstractItemModel @@ -271,6 +277,15 @@ class FlatStyle_RDM: public RetroshareDirModel QString computeDirectoryPath(const DirDetails& details) const ; + + struct CachedFileDetails { + QString name; + QString sizeStr; + QString ageStr; + QString uploadStr; + }; + QHash m_cache; + mutable RsMutex _ref_mutex ; std::vector _ref_entries ; // used to store the refs to display std::vector _ref_stack ; // used to store the refs to update