diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-12-10 16:29:41 -0500 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2020-02-04 03:33:17 -0500 |
| commit | de29b6a5a89c1db4c19cc5ed4b4946230b7a3885 (patch) | |
| tree | 6b97dc348479a53fd2fb5bb3e9aac73dc9f35d81 /src | |
| parent | efdc3eb14fc238e3cb2032148ab37f143f1bbc5f (diff) | |
IconFetcher
Diffstat (limited to 'src')
| -rw-r--r-- | src/filetree.cpp | 236 | ||||
| -rw-r--r-- | src/filetree.h | 8 |
2 files changed, 234 insertions, 10 deletions
diff --git a/src/filetree.cpp b/src/filetree.cpp index d7a61898..0d9a1414 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -83,6 +83,11 @@ QString FileTreeItem::dataRelativeFilePath() const return path += m_file; } +const QString& FileTreeItem::realPath() const +{ + return m_realPath; +} + const QString& FileTreeItem::filename() const { return m_file; @@ -154,12 +159,209 @@ QString FileTreeItem::debugName() const } +class FileTreeModel::IconFetcher +{ +public: + IconFetcher() + : m_iconSize(GetSystemMetrics(SM_CXSMICON)), m_stop(false) + { + m_quickCache.file = getPixmapIcon(QFileIconProvider::File); + m_quickCache.directory = getPixmapIcon(QFileIconProvider::Folder); + + m_thread = std::thread([&]{ threadFun(); }); + } + + ~IconFetcher() + { + stop(); + m_thread.join(); + } + + void stop() + { + m_stop = true; + m_waiter.wakeUp(); + } + + QVariant icon(const QString& path) const + { + if (hasOwnIcon(path)) { + return fileIcon(path); + } else { + const auto dot = path.lastIndexOf("."); + + if (dot == -1) { + // no extension + return m_quickCache.file; + } + + return extensionIcon(path.midRef(dot)); + } + } + + QPixmap genericFileIcon() const + { + return m_quickCache.file; + } + + QPixmap genericDirectoryIcon() const + { + return m_quickCache.directory; + } + +private: + struct QuickCache + { + QPixmap file; + QPixmap directory; + }; + + struct Cache + { + std::map<QString, QPixmap, std::less<>> map; + std::mutex mapMutex; + + std::set<QString> queue; + std::mutex queueMutex; + }; + + struct Waiter + { + mutable std::mutex m_wakeUpMutex; + std::condition_variable m_wakeUp; + bool m_queueAvailable = false; + + void wait() + { + std::unique_lock lock(m_wakeUpMutex); + m_wakeUp.wait(lock, [&]{ return m_queueAvailable; }); + m_queueAvailable = false; + } + + void wakeUp() + { + { + std::scoped_lock lock(m_wakeUpMutex); + m_queueAvailable = true; + } + + m_wakeUp.notify_one(); + } + }; + + const int m_iconSize; + QFileIconProvider m_provider; + std::thread m_thread; + std::atomic<bool> m_stop; + + mutable QuickCache m_quickCache; + mutable Cache m_extensionCache; + mutable Cache m_fileCache; + mutable Waiter m_waiter; + + + bool hasOwnIcon(const QString& path) const + { + static const QString exe = ".exe"; + static const QString lnk = ".lnk"; + static const QString ico = ".ico"; + + return + path.endsWith(exe, Qt::CaseInsensitive) || + path.endsWith(lnk, Qt::CaseInsensitive) || + path.endsWith(ico, Qt::CaseInsensitive); + } + + template <class T> + QPixmap getPixmapIcon(T&& t) const + { + return m_provider.icon(t).pixmap({m_iconSize, m_iconSize}); + } + + void threadFun() + { + while (!m_stop) { + m_waiter.wait(); + if (m_stop) { + break; + } + + checkCache(m_extensionCache); + checkCache(m_fileCache); + } + } + + void checkCache(Cache& cache) + { + std::set<QString> queue; + + { + std::scoped_lock lock(cache.queueMutex); + queue = std::move(cache.queue); + } + + if (queue.empty()) { + return; + } + + std::map<QString, QPixmap> map; + for (auto&& ext : queue) { + map.emplace(std::move(ext), getPixmapIcon(ext)); + } + + { + std::scoped_lock lock(cache.mapMutex); + for (auto&& p : map) { + cache.map.insert(std::move(p)); + } + } + } + + void queue(Cache& cache, QString path) const + { + { + std::scoped_lock lock(cache.queueMutex); + cache.queue.insert(std::move(path)); + } + + m_waiter.wakeUp(); + } + + QVariant fileIcon(const QString& path) const + { + { + std::scoped_lock lock(m_fileCache.mapMutex); + auto itor = m_fileCache.map.find(path); + if (itor != m_fileCache.map.end()) { + return itor->second; + } + } + + queue(m_fileCache, path); + return {}; + } + + QVariant extensionIcon(const QStringRef& ext) const + { + { + std::scoped_lock lock(m_extensionCache.mapMutex); + auto itor = m_extensionCache.map.find(ext); + if (itor != m_extensionCache.map.end()) { + return itor->second; + } + } + + queue(m_extensionCache, ext.toString()); + return {}; + } +}; + + FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) { - QFileIconProvider provider; - m_fileIcon = provider.icon(QFileIconProvider::File); - m_directoryIcon = provider.icon(QFileIconProvider::Folder); + m_iconFetcher.reset(new IconFetcher); + connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); } void FileTreeModel::setFlags(Flags f) @@ -496,12 +698,16 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const { if (index.column() == 0) { if (auto* item=itemFromIndex(index)) { - const auto iconType = item->icon(); - - if (iconType == QFileIconProvider::File) { - return m_fileIcon; - } else if (iconType == QFileIconProvider::Folder) { - return m_directoryIcon; + if (item->isDirectory()) { + return m_iconFetcher->genericDirectoryIcon(); + } else { + auto v = m_iconFetcher->icon(item->realPath()); + if (v.isNull()) { + m_iconPending.push_back(index); + m_iconPendingTimer.start(std::chrono::milliseconds(1)); + return m_iconFetcher->genericFileIcon(); + } + return v; } } } @@ -513,6 +719,18 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const return {}; } +void FileTreeModel::updatePendingIcons() +{ + log::debug("updating {} pending icons", m_iconPending.size()); + + for (auto&& index : m_iconPending) { + emit dataChanged(index, index, {Qt::DecorationRole}); + } + + m_iconPending.clear(); + m_iconPendingTimer.stop(); +} + QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const { if (role == Qt::DisplayRole) { diff --git a/src/filetree.h b/src/filetree.h index aabdf61b..75270d29 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -38,6 +38,7 @@ public: const QString& filename() const; const QString& mod() const; + const QString& realPath() const; QString dataRelativeParentPath() const; QString dataRelativeFilePath() const; @@ -95,11 +96,15 @@ public: Qt::ItemFlags flags(const QModelIndex& index) const override; private: + class IconFetcher; + using DirectoryIterator = std::vector<MOShared::DirectoryEntry*>::const_iterator; OrganizerCore& m_core; mutable FileTreeItem m_root; Flags m_flags; - QIcon m_fileIcon, m_directoryIcon; + std::unique_ptr<IconFetcher> m_iconFetcher; + mutable std::vector<QModelIndex> m_iconPending; + mutable QTimer m_iconPendingTimer; bool showConflicts() const; bool showArchives() const; @@ -120,6 +125,7 @@ private: FileTreeItem* itemFromIndex(const QModelIndex& index) const; void ensureLoaded(FileTreeItem* item) const; + void updatePendingIcons(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); |
