diff --git a/src/imagemodel.cpp b/src/imagemodel.cpp index 3288ae0..622dbf6 100644 --- a/src/imagemodel.cpp +++ b/src/imagemodel.cpp @@ -9,7 +9,8 @@ ImageModel::ImageModel(QObject *parent) : QAbstractListModel(parent) { - m_threadPool.setMaxThreadCount(QThread::idealThreadCount()); + // Limit 1 thread for thumbnail loading to avoid excessive disk I/O on harddrives. SSDs are so fast the extra threads don't help much anyway. + m_threadPool.setMaxThreadCount(1); //QThread::idealThreadCount() m_placeholder = QPixmap(128, 128); m_placeholder.fill(Qt::lightGray); @@ -24,12 +25,35 @@ ImageModel::~ImageModel() m_threadPool.waitForDone(); } +void ImageModel::setCacheMaxSize(int n) +{ + m_cacheMaxSize = std::max(0, n); + while (m_cacheOrder.size() > m_cacheMaxSize) + m_folderThumbnailCache.remove(m_cacheOrder.takeFirst()); +} + void ImageModel::setDirectory(const QString &path) { m_cancelFlag = true; m_threadPool.waitForDone(); m_cancelFlag = false; + // Save current folder's thumbnails to cache before discarding them + if (!m_currentDir.isEmpty()) { + ThumbnailMap map; + for (const Item &item : m_items) + if (item.loaded && !item.isFolder) + map[item.path] = item.thumbnail; + if (!map.isEmpty()) { + if (!m_cacheOrder.contains(m_currentDir)) { + m_cacheOrder.append(m_currentDir); + if (m_cacheOrder.size() > m_cacheMaxSize) + m_folderThumbnailCache.remove(m_cacheOrder.takeFirst()); + } + m_folderThumbnailCache[m_currentDir] = std::move(map); + } + } + beginResetModel(); m_items.clear(); m_pendingRows.clear(); @@ -61,9 +85,23 @@ void ImageModel::setDirectory(const QString &path) const auto files = dir.entryInfoList(); + // Restore cached thumbnails for this folder if available + const ThumbnailMap *cached = m_folderThumbnailCache.contains(path) + ? &m_folderThumbnailCache[path] + : nullptr; + for (const QFileInfo &file : files) { - m_items.append({file.absoluteFilePath(), file.fileName(), m_placeholder, false, false}); + QPixmap thumb = m_placeholder; + bool loaded = false; + if (cached) { + auto it = cached->find(file.absoluteFilePath()); + if (it != cached->end()) { + thumb = it.value(); + loaded = true; + } + } + m_items.append({file.absoluteFilePath(), file.fileName(), thumb, loaded, false}); } endResetModel(); diff --git a/src/imagemodel.h b/src/imagemodel.h index 7586159..0be4ed7 100644 --- a/src/imagemodel.h +++ b/src/imagemodel.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -24,6 +26,7 @@ public: QString currentDirectory() const; void requestThumbnails(int firstRow, int lastRow); + void setCacheMaxSize(int n); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; @@ -42,6 +45,8 @@ private: void queueRow(int row); + using ThumbnailMap = QHash; + QVector m_items; QThreadPool m_threadPool; QPixmap m_placeholder; @@ -50,4 +55,9 @@ private: std::atomic_bool m_cancelFlag{false}; QIcon m_folderIcon; QString m_currentDir; + + // Folder thumbnail cache (last N visited directories) + QHash m_folderThumbnailCache; + QList m_cacheOrder; + int m_cacheMaxSize = 4; }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 312c92f..2086083 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -439,6 +439,8 @@ void MainWindow::loadPreferences() m_imageView->setBackgroundColor(m_backgroundColorPreference); m_lockDockingPreference = settings.value("lockDocking", false).toBool(); applyDockLocking(m_lockDockingPreference); + m_folderCacheSizePreference = settings.value("folderCacheSize", 4).toInt(); + m_imageModel->setCacheMaxSize(m_folderCacheSizePreference); if (settings.contains("windowGeometry")) restoreGeometry(settings.value("windowGeometry").toByteArray()); if (settings.contains("windowState")) @@ -450,6 +452,7 @@ void MainWindow::savePreferences() QSettings settings("SagoImageBrowser", "SagoImageBrowser"); settings.setValue("backgroundColor", m_backgroundColorPreference); settings.setValue("lockDocking", m_lockDockingPreference); + settings.setValue("folderCacheSize", m_folderCacheSizePreference); settings.setValue("windowGeometry", saveGeometry()); settings.setValue("windowState", saveState()); } @@ -459,6 +462,7 @@ void MainWindow::onPreferencesTriggered() PreferencesDialog dialog(this); dialog.setBackgroundColor(m_backgroundColorPreference); dialog.setLockDocking(m_lockDockingPreference); + dialog.setCacheSize(m_folderCacheSizePreference); bool shouldResetLayout = false; connect(&dialog, &PreferencesDialog::resetLayoutRequested, this, [&shouldResetLayout]() { @@ -471,6 +475,8 @@ void MainWindow::onPreferencesTriggered() m_imageView->setBackgroundColor(m_backgroundColorPreference); m_lockDockingPreference = dialog.getLockDocking(); applyDockLocking(m_lockDockingPreference); + m_folderCacheSizePreference = dialog.getCacheSize(); + m_imageModel->setCacheMaxSize(m_folderCacheSizePreference); savePreferences(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 76bcf9e..39f127d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -52,6 +52,7 @@ private: QStackedWidget *m_stack; QString m_backgroundColorPreference = "black"; + int m_folderCacheSizePreference = 4; QWidget *m_browserPage; ImageViewWidget *m_imageView; bool m_wasMaximized = false; diff --git a/src/preferencesdialog.cpp b/src/preferencesdialog.cpp index 32de6c5..a96b863 100644 --- a/src/preferencesdialog.cpp +++ b/src/preferencesdialog.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,17 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) bgColorLayout->addStretch(); groupLayout->addLayout(bgColorLayout); + + QHBoxLayout *cacheSizeLayout = new QHBoxLayout; + QLabel *cacheSizeLabel = new QLabel("Folder thumbnail cache (folders):"); + m_cacheSizeSpinBox = new QSpinBox; + m_cacheSizeSpinBox->setRange(1, 20); + m_cacheSizeSpinBox->setValue(4); + cacheSizeLayout->addWidget(cacheSizeLabel); + cacheSizeLayout->addWidget(m_cacheSizeSpinBox); + cacheSizeLayout->addStretch(); + groupLayout->addLayout(cacheSizeLayout); + mainLayout->addWidget(imageGroup); QGroupBox *layoutGroup = new QGroupBox("Layout", this); @@ -76,6 +88,16 @@ void PreferencesDialog::setBackgroundColor(const QString &color) m_backgroundColorCombo->setCurrentIndex(index); } +int PreferencesDialog::getCacheSize() const +{ + return m_cacheSizeSpinBox->value(); +} + +void PreferencesDialog::setCacheSize(int n) +{ + m_cacheSizeSpinBox->setValue(n); +} + bool PreferencesDialog::getLockDocking() const { return m_lockDockingCheck->isChecked(); diff --git a/src/preferencesdialog.h b/src/preferencesdialog.h index 391fc8d..a6b79cf 100644 --- a/src/preferencesdialog.h +++ b/src/preferencesdialog.h @@ -4,6 +4,7 @@ class QComboBox; class QCheckBox; +class QSpinBox; class PreferencesDialog : public QDialog { @@ -15,6 +16,9 @@ public: QString getBackgroundColor() const; void setBackgroundColor(const QString &color); + int getCacheSize() const; + void setCacheSize(int n); + bool getLockDocking() const; void setLockDocking(bool lock); @@ -26,6 +30,7 @@ signals: private: QComboBox *m_backgroundColorCombo; + QSpinBox *m_cacheSizeSpinBox; QCheckBox *m_lockDockingCheck; QCheckBox *m_resetLayoutCheck; };