diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 4af4f3024..4fd126619 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -422,6 +422,10 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList &itemRang m_hoverSequenceLoadedItems.clear(); killPreviewJob(); + + if (m_scanDirectories) { + m_directoryContentsCounter->stopWorker(); + } } else { // Only remove the items from m_finishedItems. They will be removed // from the other sets later on. @@ -537,7 +541,7 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem &item, const QPi QPixmap scaledPixmap = transformPreviewPixmap(pixmap); - QHash data = rolesData(item); + QHash data = rolesData(item, index); const QStringList overlays = data["iconOverlays"].toStringList(); // Strangely KFileItem::overlays() returns empty string-values, so @@ -1210,13 +1214,10 @@ void KFileItemModelRolesUpdater::applySortRole(int index) data.insert("type", item.mimeComment()); } else if (m_model->sortRole() == "size" && item.isLocalFile() && !item.isSlow() && item.isDir()) { - const QString path = item.localPath(); - if (m_scanDirectories) { - m_directoryContentsCounter->scanDirectory(path); - } + startDirectorySizeCounting(item, index); } else { // Probably the sort role is a baloo role - just determine all roles. - data = rolesData(item); + data = rolesData(item, index); } disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); @@ -1252,7 +1253,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) QHash data; if (resolveAll) { - data = rolesData(item); + data = rolesData(item, index); } if (!item.iconName().isEmpty()) { @@ -1273,7 +1274,19 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) return false; } -QHash KFileItemModelRolesUpdater::rolesData(const KFileItem &item) +void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index) +{ + // Tell m_directoryContentsCounter that we want to count the items + // inside the directory. The result will be received in slotDirectoryContentsCountReceived. + if (m_scanDirectories && item.isLocalFile()) { + const QString path = item.localPath(); + const auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High + : KDirectoryContentsCounter::PathCountPriority::Normal; + m_directoryContentsCounter->scanDirectory(path, priority); + } +} + +QHash KFileItemModelRolesUpdater::rolesData(const KFileItem &item, int index) { QHash data; @@ -1281,16 +1294,7 @@ QHash KFileItemModelRolesUpdater::rolesData(const KFileIte const bool getIsExpandableRole = m_roles.contains("isExpandable"); if ((getSizeRole || getIsExpandableRole) && !item.isSlow() && item.isDir()) { - if (item.isLocalFile()) { - // Tell m_directoryContentsCounter that we want to count the items - // inside the directory. The result will be received in slotDirectoryContentsCountReceived. - if (m_scanDirectories) { - const QString path = item.localPath(); - m_directoryContentsCounter->scanDirectory(path); - } - } else if (getSizeRole) { - data.insert("size", -1); // -1 indicates an unknown number of items - } + startDirectorySizeCounting(item, index); } if (m_roles.contains("extension")) { diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h index 90f29c5c0..be6f23193 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -319,7 +319,7 @@ private: enum ResolveHint { ResolveFast, ResolveAll }; bool applyResolvedRoles(int index, ResolveHint hint); - QHash rolesData(const KFileItem &item); + QHash rolesData(const KFileItem &item, int index); /** * Must be invoked if a property has been changed that affects @@ -334,6 +334,8 @@ private: void trimHoverSequenceLoadedItems(); private: + void startDirectorySizeCounting(const KFileItem &item, int index); + enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning }; State m_state; diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index 039b79b6e..37e852ab9 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -44,6 +44,7 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel *model, QObj m_worker->moveToThread(m_workerThread); connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); + connect(this, &KDirectoryContentsCounter::stop, m_worker, &KDirectoryContentsCounterWorker::stop); connect(m_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult); m_dirWatcher = new KDirWatch(this); @@ -71,11 +72,6 @@ KDirectoryContentsCounter::~KDirectoryContentsCounter() } } -void KDirectoryContentsCounter::scanDirectory(const QString &path) -{ - startWorker(path); -} - void KDirectoryContentsCounter::slotResult(const QString &path, int count, long size) { m_workerIsBusy = false; @@ -91,11 +87,11 @@ void KDirectoryContentsCounter::slotResult(const QString &path, int count, long if (!m_priorityQueue.empty()) { const QString firstPath = m_priorityQueue.front(); m_priorityQueue.pop_front(); - startWorker(firstPath); + scanDirectory(firstPath, PathCountPriority::High); } else if (!m_queue.empty()) { const QString firstPath = m_queue.front(); m_queue.pop_front(); - startWorker(firstPath); + scanDirectory(firstPath, PathCountPriority::Normal); } if (s_cache->contains(resolvedPath)) { @@ -127,7 +123,7 @@ void KDirectoryContentsCounter::slotDirWatchDirty(const QString &path) return; } - startWorker(path); + scanDirectory(path, PathCountPriority::High); } } @@ -156,7 +152,7 @@ void KDirectoryContentsCounter::slotItemsRemoved() } } -void KDirectoryContentsCounter::startWorker(const QString &path) +void KDirectoryContentsCounter::scanDirectory(const QString &path, PathCountPriority priority) { const QString resolvedPath = QFileInfo(path).canonicalFilePath(); const bool alreadyInCache = s_cache->contains(resolvedPath); @@ -168,10 +164,16 @@ void KDirectoryContentsCounter::startWorker(const QString &path) } if (m_workerIsBusy) { + // only enqueue path not yet in queue if (std::find(m_queue.begin(), m_queue.end(), path) == m_queue.end() && std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path) == m_priorityQueue.end()) { - if (alreadyInCache) { - m_queue.push_back(path); + if (priority == PathCountPriority::Normal) { + if (alreadyInCache) { + // if we already knew the dir size, it gets lower priority + m_queue.push_back(path); + } else { + m_queue.push_front(path); + } } else { // append to priority queue m_priorityQueue.push_back(path); @@ -193,4 +195,11 @@ void KDirectoryContentsCounter::startWorker(const QString &path) } } +void KDirectoryContentsCounter::stopWorker() +{ + m_queue.clear(); + m_priorityQueue.clear(); + Q_EMIT stop(); +} + QThread *KDirectoryContentsCounter::m_workerThread = nullptr; diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h index f74565b73..9a5e4a86b 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -21,6 +21,8 @@ class KDirectoryContentsCounter : public QObject Q_OBJECT public: + enum PathCountPriority { Normal, High }; + explicit KDirectoryContentsCounter(KFileItemModel *model, QObject *parent = nullptr); ~KDirectoryContentsCounter() override; @@ -35,7 +37,12 @@ public: * Uses a cache internally to speed up first result, * but emit again result when the cache was updated */ - void scanDirectory(const QString &path); + void scanDirectory(const QString &path, PathCountPriority priority); + + /** + * Stops the work until new input is passed + */ + void stopWorker(); Q_SIGNALS: /** @@ -46,14 +53,13 @@ Q_SIGNALS: void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options); + void stop(); + private Q_SLOTS: void slotResult(const QString &path, int count, long size); void slotDirWatchDirty(const QString &path); void slotItemsRemoved(); -private: - void startWorker(const QString &path); - private: KFileItemModel *m_model; diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index e227c1bc6..2227ebbc2 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -25,7 +25,7 @@ KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent #ifndef Q_OS_WIN KDirectoryContentsCounterWorker::CountResult -walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel) +KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel) { int count = -1; long size = -1; @@ -36,7 +36,7 @@ walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDir QT_DIRENT *dirEntry; QT_STATBUF buf; - while ((dirEntry = QT_READDIR(dir))) { + while (!m_stopping && (dirEntry = QT_READDIR(dir))) { if (dirEntry->d_name[0] == '.') { if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { // Skip "." or hidden files @@ -64,7 +64,7 @@ walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDir size += buf.st_size; } } - if (dirEntry->d_type == DT_DIR) { + if (!m_stopping && dirEntry->d_type == DT_DIR) { // recursion for dirs auto subdirResult = walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, allowedRecursiveLevel - 1); if (subdirResult.size > 0) { @@ -106,8 +106,18 @@ KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::su #endif } +void KDirectoryContentsCounterWorker::stop() +{ + m_stopping = true; +} + void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options) { + m_stopping = false; + auto res = subItemsCount(path, options); - Q_EMIT result(path, res.count, res.size); + + if (!m_stopping) { + Q_EMIT result(path, res.count, res.size); + } } diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h index 6bbe099a5..5266960cd 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.h +++ b/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -36,7 +36,7 @@ public: * * @return The number of items. */ - static CountResult subItemsCount(const QString &path, Options options); + CountResult subItemsCount(const QString &path, Options options); Q_SIGNALS: /** @@ -53,6 +53,15 @@ public Q_SLOTS: // is needed here. Just using 'Options' is OK for the compiler, but // confuses moc. void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options); + void stop(); + +private: +#ifndef Q_OS_WIN + KDirectoryContentsCounterWorker::CountResult + walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel); +#endif + + bool m_stopping = false; }; Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options)