plugin/fsm: async update

This commit is contained in:
2 * r + 2 * t 2025-09-02 16:59:24 +10:00
parent 2601955dfd
commit c0c4e14e58
4 changed files with 104 additions and 57 deletions

View file

@ -1,4 +1,4 @@
find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui) find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui Concurrent)
if(QT_KNOWN_POLICY_QTP0001) if(QT_KNOWN_POLICY_QTP0001)
qt_policy(SET QTP0001 NEW) qt_policy(SET QTP0001 NEW)

View file

@ -24,4 +24,4 @@ install(TARGETS "${module_plugin_target}" LIBRARY DESTINATION "${module_dir}" RU
install(FILES "${module_qmldir}" DESTINATION "${module_dir}") install(FILES "${module_qmldir}" DESTINATION "${module_dir}")
install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") install(FILES "${module_typeinfo}" DESTINATION "${module_dir}")
target_link_libraries(caelestia PRIVATE Qt::Core Qt::Qml Qt::Gui) target_link_libraries(caelestia PRIVATE Qt::Core Qt::Qml Qt::Gui Qt::Concurrent)

View file

@ -4,8 +4,10 @@
#include <QDir> #include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QFileInfo> #include <QFileInfo>
#include <QFutureWatcher>
#include <QImageReader> #include <QImageReader>
#include <QObject> #include <QObject>
#include <QtConcurrent>
#include <qqmlintegration.h> #include <qqmlintegration.h>
int FileSystemModel::rowCount(const QModelIndex& parent) const { int FileSystemModel::rowCount(const QModelIndex& parent) const {
@ -116,77 +118,120 @@ void FileSystemModel::updateEntries() {
return; return;
} }
for (auto& future : m_futures) {
future.cancel();
}
m_futures.clear();
updateEntriesForDir(m_path); updateEntriesForDir(m_path);
} }
void FileSystemModel::updateEntriesForDir(const QString& dir) { void FileSystemModel::updateEntriesForDir(const QString& dir) {
const auto flags = m_recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags; const bool recursive = m_recursive;
const auto filter = m_filter;
const auto oldEntries = m_entries;
const auto baseDir = m_dir;
std::optional<QDirIterator> iter; const auto future = QtConcurrent::run(
[dir, recursive, filter, oldEntries, baseDir](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
if (m_filter == Images) { std::optional<QDirIterator> iter;
QStringList filters;
for (const auto& format : QImageReader::supportedImageFormats()) { if (filter == Images) {
filters << "*." + format; QStringList filters;
for (const auto& format : QImageReader::supportedImageFormats()) {
filters << "*." + format;
}
iter.emplace(dir, filters, QDir::Files, flags);
} else if (filter == Files) {
iter.emplace(dir, QDir::Files, flags);
} else {
iter.emplace(dir, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, flags);
}
QSet<QString> newPaths;
while (iter->hasNext()) {
if (promise.isCanceled()) {
return;
}
QString path = iter->next();
if (filter == Images) {
QImageReader reader(path);
if (!reader.canRead()) {
continue;
}
}
newPaths.insert(path);
}
QSet<QString> oldPaths;
for (const auto& entry : oldEntries) {
oldPaths.insert(entry->path());
}
if (promise.isCanceled() || newPaths == oldPaths) {
return;
}
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
});
if (m_futures.contains(dir)) {
m_futures[dir].cancel();
}
m_futures.insert(dir, future);
const auto watcher = new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
connect(watcher, &QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished, this, [watcher, this]() {
if (!watcher->future().isResultReadyAt(0)) {
watcher->deleteLater();
return;
} }
iter.emplace(dir, filters, QDir::Files, flags); QElapsedTimer timer;
} else if (m_filter == Files) { timer.start();
iter.emplace(dir, QDir::Files, flags);
} else {
iter.emplace(dir, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, flags);
}
QSet<QString> newPaths; const auto result = watcher->result();
while (iter->hasNext()) { const auto removedPaths = result.first;
QString path = iter->next(); const auto addedPaths = result.second;
if (m_filter == Images) { beginResetModel();
QImageReader reader(path);
if (!reader.canRead()) { const int numEntries = static_cast<int>(m_entries.size());
continue; for (int i = numEntries - 1; i >= 0; --i) {
if (removedPaths.contains(m_entries[i]->path())) {
emit removed(m_entries[i]->path());
delete m_entries.takeAt(i);
} }
} }
newPaths.insert(path); for (const auto& path : addedPaths) {
} const auto entry = new FileSystemEntry(path, m_dir.relativeFilePath(path), this);
emit added(entry);
QSet<QString> oldPaths; m_entries << entry;
for (const auto& entry : m_entries) {
oldPaths.insert(entry->path());
}
if (newPaths == oldPaths) {
return;
}
const QSet<QString> removedPaths = oldPaths - newPaths;
const QSet<QString> addedPaths = newPaths - oldPaths;
beginResetModel();
const int numEntries = static_cast<int>(m_entries.size());
for (int i = numEntries - 1; i >= 0; --i) {
if (removedPaths.contains(m_entries[i]->path())) {
emit removed(m_entries[i]->path());
delete m_entries.takeAt(i);
} }
}
for (const auto& path : addedPaths) { std::sort(m_entries.begin(), m_entries.end(), [](const FileSystemEntry* a, const FileSystemEntry* b) {
const auto entry = new FileSystemEntry(path, m_dir.relativeFilePath(path), this); if (a->isDir() != b->isDir()) {
emit added(entry); return a->isDir();
m_entries << entry; }
} return a->relativePath().localeAwareCompare(b->relativePath()) < 0;
});
std::sort(m_entries.begin(), m_entries.end(), [](const FileSystemEntry* a, const FileSystemEntry* b) { emit entriesChanged();
if (a->isDir() != b->isDir()) {
return a->isDir(); endResetModel();
}
return a->relativePath().localeAwareCompare(b->relativePath()) < 0; watcher->deleteLater();
qDebug() << "Update took" << timer.elapsed() << "ms";
}); });
emit entriesChanged(); watcher->setFuture(future);
endResetModel();
} }

View file

@ -4,6 +4,7 @@
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QFuture>
#include <QImageReader> #include <QImageReader>
#include <QObject> #include <QObject>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@ -109,6 +110,7 @@ private:
QDir m_dir; QDir m_dir;
QFileSystemWatcher m_watcher; QFileSystemWatcher m_watcher;
QList<FileSystemEntry*> m_entries; QList<FileSystemEntry*> m_entries;
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString>>>> m_futures;
QString m_path; QString m_path;
bool m_recursive; bool m_recursive;