diff --git a/wallpapers/image/plugin/model/abstractimagelistmodel.cpp b/wallpapers/image/plugin/model/abstractimagelistmodel.cpp index 8d58ef6db..368a1efc8 100644 --- a/wallpapers/image/plugin/model/abstractimagelistmodel.cpp +++ b/wallpapers/image/plugin/model/abstractimagelistmodel.cpp @@ -6,6 +6,7 @@ #include "abstractimagelistmodel.h" +#include #include #include @@ -72,48 +73,95 @@ void AbstractImageListModel::slotHandleImageSizeFound(const QString &path, const void AbstractImageListModel::slotHandlePreview(const KFileItem &item, const QPixmap &preview) { + auto job = qobject_cast(sender()); + if (!job) { + return; + } + const QString urlString = item.url().toLocalFile(); - const QPersistentModelIndex pidx = m_previewJobsUrls.take(urlString); - QModelIndex idx; + const QPersistentModelIndex idx = job->property("index").toPersistentModelIndex(); - if (!pidx.isValid()) { - if (int row = indexOf(urlString); row >= 0) { - idx = index(row, 0); - } else { - return; - } + auto it = m_previewJobsUrls.find(idx); + Q_ASSERT(it != m_previewJobsUrls.end()); + it->removeOne(urlString); + + const QStringList paths = job->property("paths").toStringList(); + QPixmap *cachedPreview = m_imageCache.object(paths); + + if (!cachedPreview) { + // Insert full preview + m_imageCache.insert(paths, new QPixmap(preview), 0); } else { - idx = pidx; + // Show multiple images side by side + QPainter p(cachedPreview); + + const int i = paths.indexOf(urlString); + const double start = i / static_cast(paths.size()); + const double end = (i + 1) / static_cast(paths.size()); + // Cropped area + const QPoint topLeft(start * preview.width(), 0); + const QPoint bottomRight(end * preview.width(), preview.height()); + // Inserted area + const QPoint topLeft2(start * cachedPreview->width(), 0); + const QPoint bottomRight2(end * cachedPreview->width(), cachedPreview->height()); + + p.drawPixmap(QRect(topLeft2, bottomRight2), preview.copy(QRect(topLeft, bottomRight))); } - const int cost = preview.width() * preview.height() * preview.depth() / 8; + if (it->empty()) { + // All images in the list have been loaded + m_previewJobsUrls.erase(it); - if (m_imageCache.insert(urlString, new QPixmap(preview), cost)) { - Q_EMIT dataChanged(idx, idx, {ScreenshotRole}); + cachedPreview = m_imageCache.object(paths); + auto finalPreview = new QPixmap(*cachedPreview); + + if (m_imageCache.insert(paths, finalPreview, 1)) { + Q_EMIT dataChanged(idx, idx, {ScreenshotRole}); + } else { + delete finalPreview; + } } } void AbstractImageListModel::slotHandlePreviewFailed(const KFileItem &item) { - m_previewJobsUrls.remove(item.url().toLocalFile()); + auto job = qobject_cast(sender()); + if (!job) { + return; + } + + auto it = m_previewJobsUrls.find(job->property("index").toPersistentModelIndex()); + Q_ASSERT(it != m_previewJobsUrls.end()); + + it->removeOne(item.url().toLocalFile()); + if (it->empty()) { + m_previewJobsUrls.erase(it); + } } -void AbstractImageListModel::asyncGetPreview(const QString &path, const QPersistentModelIndex &index) const +void AbstractImageListModel::asyncGetPreview(const QStringList &paths, const QPersistentModelIndex &index) const { - if (m_previewJobsUrls.contains(path) || path.isEmpty()) { + if (m_previewJobsUrls.contains(index) || paths.isEmpty()) { return; } - const QUrl url = QUrl::fromLocalFile(path); const QStringList availablePlugins = KIO::PreviewJob::availablePlugins(); + KFileItemList list; + + for (const QString &path : paths) { + list.append(KFileItem(QUrl::fromLocalFile(path), QString(), 0)); + } - KIO::PreviewJob *const job = KIO::filePreview(KFileItemList{KFileItem(url, QString(), 0)}, m_screenshotSize, &availablePlugins); + KIO::PreviewJob *const job = KIO::filePreview(list, m_screenshotSize, &availablePlugins); job->setIgnoreMaximumSize(true); + job->setProperty("paths", paths); + job->setProperty("index", index); + connect(job, &KIO::PreviewJob::gotPreview, this, &AbstractImageListModel::slotHandlePreview); connect(job, &KIO::PreviewJob::failed, this, &AbstractImageListModel::slotHandlePreviewFailed); - m_previewJobsUrls.insert(path, index); + m_previewJobsUrls.insert(index, paths); } void AbstractImageListModel::asyncGetImageSize(const QString &path, const QPersistentModelIndex &index) const diff --git a/wallpapers/image/plugin/model/abstractimagelistmodel.h b/wallpapers/image/plugin/model/abstractimagelistmodel.h index bafb9849d..581552e0e 100644 --- a/wallpapers/image/plugin/model/abstractimagelistmodel.h +++ b/wallpapers/image/plugin/model/abstractimagelistmodel.h @@ -53,7 +53,13 @@ Q_SIGNALS: void loaded(AbstractImageListModel *model); protected: - void asyncGetPreview(const QString &path, const QPersistentModelIndex &index) const; + /** + * Asynchronously generates a preview. + * Multiple images are displayed side by side following the order in @c paths + * + * @note @c paths should have no duplicate urls. + */ + void asyncGetPreview(const QStringList &paths, const QPersistentModelIndex &index) const; void asyncGetImageSize(const QString &path, const QPersistentModelIndex &index) const; bool m_loading = false; @@ -61,10 +67,10 @@ protected: QSize m_screenshotSize; QSize m_targetSize; - QCache m_imageCache; + QCache m_imageCache; QCache m_imageSizeCache; - mutable QHash m_previewJobsUrls; + mutable QHash m_previewJobsUrls; mutable QHash m_sizeJobsUrls; QHash m_pendingDeletion; diff --git a/wallpapers/image/plugin/model/imagelistmodel.cpp b/wallpapers/image/plugin/model/imagelistmodel.cpp index 1bfd15d0f..73a9d02bc 100644 --- a/wallpapers/image/plugin/model/imagelistmodel.cpp +++ b/wallpapers/image/plugin/model/imagelistmodel.cpp @@ -41,13 +41,13 @@ QVariant ImageListModel::data(const QModelIndex &index, int role) const return QFileInfo(m_data.at(row)).completeBaseName(); case ScreenshotRole: { - QPixmap *cachedPreview = m_imageCache.object(m_data.at(row)); + QPixmap *cachedPreview = m_imageCache.object({m_data.at(row)}); if (cachedPreview) { return *cachedPreview; } - asyncGetPreview(m_data.at(row), QPersistentModelIndex(index)); + asyncGetPreview({m_data.at(row)}, QPersistentModelIndex(index)); return QVariant(); } diff --git a/wallpapers/image/plugin/model/packagelistmodel.cpp b/wallpapers/image/plugin/model/packagelistmodel.cpp index a49196a23..cd2aa1597 100644 --- a/wallpapers/image/plugin/model/packagelistmodel.cpp +++ b/wallpapers/image/plugin/model/packagelistmodel.cpp @@ -46,15 +46,20 @@ QVariant PackageListModel::data(const QModelIndex &index, int role) const return PackageFinder::packageDisplayName(b); case ScreenshotRole: { - const QString path = b.filePath("preferred"); + QStringList paths{b.filePath(QByteArrayLiteral("preferred"))}; + const QString darkPath = b.filePath(QByteArrayLiteral("preferredDark")); + + if (!darkPath.isEmpty()) { + paths.append(darkPath); + } - QPixmap *cachedPreview = m_imageCache.object(path); + QPixmap *cachedPreview = m_imageCache.object(paths); if (cachedPreview) { return *cachedPreview; } - asyncGetPreview(path, QPersistentModelIndex(index)); + asyncGetPreview(paths, QPersistentModelIndex(index)); return QVariant(); }