Optimized Shared Files view performance by caching file details to eliminate UI freezes and increasing the update frequency for faster list population

This commit is contained in:
jolavillette 2026-01-26 21:07:46 +01:00
parent ba1d213d5c
commit 37f4ddc3d0
3 changed files with 135 additions and 10 deletions

View File

@ -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<std::string>(), 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();
}
}
}
}

View File

@ -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<std::string>& 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() ;
}

View File

@ -21,6 +21,8 @@
#ifndef REMOTE_DIR_MODEL
#define REMOTE_DIR_MODEL
#define RETROSHARE_DIR_MODEL_FILTER_STRING "filtered"
#include <retroshare/rstypes.h>
#include <retroshare/rsevents.h>
@ -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<void*, CachedFileDetails> m_cache;
mutable RsMutex _ref_mutex ;
std::vector<void *> _ref_entries ; // used to store the refs to display
std::vector<void *> _ref_stack ; // used to store the refs to update