refactor: delete batch loading part
This commit is contained in:
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 01:34:52
|
* @Date: 2025-08-05 01:34:52
|
||||||
* @LastEditTime: 2025-08-06 02:34:10
|
* @LastEditTime: 2025-08-07 00:09:51
|
||||||
* @Description: Configuration manager.
|
* @Description: Configuration manager.
|
||||||
*/
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@@ -211,6 +211,7 @@ bool Config::isValidImageFile(const QString &filePath) {
|
|||||||
static const QStringList validExtensions = {
|
static const QStringList validExtensions = {
|
||||||
".jpg",
|
".jpg",
|
||||||
".jpeg",
|
".jpeg",
|
||||||
|
".jfif",
|
||||||
".png",
|
".png",
|
||||||
".bmp",
|
".bmp",
|
||||||
".gif",
|
".gif",
|
||||||
|
|||||||
+26
-39
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 01:22:53
|
* @Date: 2025-08-05 01:22:53
|
||||||
* @LastEditTime: 2025-08-06 22:44:49
|
* @LastEditTime: 2025-08-07 00:22:29
|
||||||
* @Description: Animated carousel widget for displaying and selecting images.
|
* @Description: Animated carousel widget for displaying and selecting images.
|
||||||
*/
|
*/
|
||||||
#include "images_carousel.h"
|
#include "images_carousel.h"
|
||||||
@@ -9,7 +9,9 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <qboxlayout.h>
|
#include <qboxlayout.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
#include <qnamespace.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
#include <qpixmap.h>
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
@@ -31,7 +33,6 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
|
|||||||
QWidget* parent)
|
QWidget* parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
ui(new Ui::ImagesCarousel),
|
ui(new Ui::ImagesCarousel),
|
||||||
m_updateTimer(new QTimer(this)),
|
|
||||||
m_itemWidth(itemWidth),
|
m_itemWidth(itemWidth),
|
||||||
m_itemHeight(static_cast<int>(itemWidth / itemAspectRatio)),
|
m_itemHeight(static_cast<int>(itemWidth / itemAspectRatio)),
|
||||||
m_itemFocusWidth(itemFocusWidth),
|
m_itemFocusWidth(itemFocusWidth),
|
||||||
@@ -44,23 +45,19 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
|
|||||||
m_imagesLayout = dynamic_cast<QHBoxLayout*>(ui->scrollAreaWidgetContents->layout());
|
m_imagesLayout = dynamic_cast<QHBoxLayout*>(ui->scrollAreaWidgetContents->layout());
|
||||||
|
|
||||||
// Load initial images
|
// Load initial images
|
||||||
connect(m_updateTimer,
|
|
||||||
&QTimer::timeout,
|
|
||||||
this,
|
|
||||||
&ImagesCarousel::_updateImages);
|
|
||||||
connect(this,
|
connect(this,
|
||||||
&ImagesCarousel::imagesLoaded,
|
&ImagesCarousel::imagesLoaded,
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
_focusCurrImage();
|
_focusCurrImage();
|
||||||
|
// exit(0); // for speed test
|
||||||
disconnect(this, &ImagesCarousel::imagesLoaded, this, nullptr);
|
disconnect(this, &ImagesCarousel::imagesLoaded, this, nullptr);
|
||||||
});
|
});
|
||||||
m_updateTimer->start(100);
|
|
||||||
|
|
||||||
// Auto focus when scrolling
|
// Auto focus when scrolling
|
||||||
m_scrollDebounceTimer = new QTimer(this);
|
m_scrollDebounceTimer = new QTimer(this);
|
||||||
m_scrollDebounceTimer->setSingleShot(true);
|
m_scrollDebounceTimer->setSingleShot(true);
|
||||||
m_scrollDebounceTimer->setInterval(200);
|
m_scrollDebounceTimer->setInterval(s_debounceInterval);
|
||||||
connect(m_scrollDebounceTimer,
|
connect(m_scrollDebounceTimer,
|
||||||
&QTimer::timeout,
|
&QTimer::timeout,
|
||||||
this,
|
this,
|
||||||
@@ -81,9 +78,6 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
|
|||||||
|
|
||||||
ImagesCarousel::~ImagesCarousel() {
|
ImagesCarousel::~ImagesCarousel() {
|
||||||
delete ui;
|
delete ui;
|
||||||
for (auto item : std::as_const(m_imageQueue)) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
// memory of items in m_loadedImages managed by Qt parent-child system
|
// memory of items in m_loadedImages managed by Qt parent-child system
|
||||||
// ...
|
// ...
|
||||||
if (m_scrollAnimation) {
|
if (m_scrollAnimation) {
|
||||||
@@ -111,20 +105,14 @@ ImageLoader::ImageLoader(const QString& path, ImagesCarousel* carousel)
|
|||||||
setAutoDelete(true);
|
setAutoDelete(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::_addImageToQueue(const ImageData* data) {
|
void ImagesCarousel::_insertImage(const ImageData* data) {
|
||||||
QMutexLocker locker(&m_queueMutex);
|
auto item = new ImageItem(
|
||||||
auto imageItem = new ImageItem(
|
|
||||||
data,
|
data,
|
||||||
m_itemWidth,
|
m_itemWidth,
|
||||||
m_itemHeight,
|
m_itemHeight,
|
||||||
m_itemFocusWidth,
|
m_itemFocusWidth,
|
||||||
m_itemFocusHeight,
|
m_itemFocusHeight,
|
||||||
this);
|
this);
|
||||||
m_imageQueue.enqueue(imageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImagesCarousel::_updateImages() {
|
|
||||||
QMutexLocker locker(&m_queueMutex);
|
|
||||||
|
|
||||||
static const QVector<std::function<bool(const ImageItem*, const ImageItem*)>> cmpFuncs = {
|
static const QVector<std::function<bool(const ImageItem*, const ImageItem*)>> cmpFuncs = {
|
||||||
[](auto, auto) {
|
[](auto, auto) {
|
||||||
@@ -141,10 +129,6 @@ void ImagesCarousel::_updateImages() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int processCount = 0;
|
|
||||||
while (!m_imageQueue.isEmpty() && processCount < 5) {
|
|
||||||
ImageItem* item = m_imageQueue.dequeue();
|
|
||||||
|
|
||||||
// insert into correct position based on sort type and direction
|
// insert into correct position based on sort type and direction
|
||||||
// currently O(n^2), but better as O(n * (n + log(n))) with vector and binary search
|
// currently O(n^2), but better as O(n * (n + log(n))) with vector and binary search
|
||||||
qint64 inserPos = m_loadedImages.size();
|
qint64 inserPos = m_loadedImages.size();
|
||||||
@@ -155,16 +139,12 @@ void ImagesCarousel::_updateImages() {
|
|||||||
(*it)->m_index++, ++it, --inserPos);
|
(*it)->m_index++, ++it, --inserPos);
|
||||||
}
|
}
|
||||||
item->m_index = inserPos;
|
item->m_index = inserPos;
|
||||||
connect(item, &ImageItem::clicked, this, [this](int index) {
|
connect(item,
|
||||||
// if (m_suppressAutoFocus) return;
|
&ImageItem::clicked,
|
||||||
_unfocusCurrImage();
|
this,
|
||||||
m_currentIndex = index;
|
&ImagesCarousel::_onItemClicked);
|
||||||
_focusCurrImage();
|
|
||||||
});
|
|
||||||
m_loadedImages.insert(inserPos, item);
|
m_loadedImages.insert(inserPos, item);
|
||||||
m_imagesLayout->insertWidget(inserPos, item);
|
m_imagesLayout->insertWidget(inserPos, item);
|
||||||
processCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker countLocker(&m_imageCountMutex);
|
QMutexLocker countLocker(&m_imageCountMutex);
|
||||||
@@ -177,23 +157,23 @@ void ImagesCarousel::_updateImages() {
|
|||||||
void ImageLoader::run() {
|
void ImageLoader::run() {
|
||||||
auto data = new ImageData(m_path, m_initWidth, m_initHeight);
|
auto data = new ImageData(m_path, m_initWidth, m_initHeight);
|
||||||
QMetaObject::invokeMethod(m_carousel,
|
QMetaObject::invokeMethod(m_carousel,
|
||||||
"_addImageToQueue",
|
"_insertImage",
|
||||||
Qt::QueuedConnection,
|
Qt::QueuedConnection,
|
||||||
Q_ARG(const ImageData*, data));
|
Q_ARG(const ImageData*, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageData::ImageData(const QString& p, const int initWidth, const int initHeight) : file(p) {
|
ImageData::ImageData(const QString& p, const int initWidth, const int initHeight) : file(p) {
|
||||||
if (!pixmap.load(p)) {
|
if (!image.load(p)) {
|
||||||
warn(QString("Failed to load image from path: %1").arg(p));
|
warn(QString("Failed to load image from path: %1").arg(p));
|
||||||
}
|
}
|
||||||
// resize in "cover" mode
|
// resize in "cover" mode
|
||||||
const QSize targetSize(initWidth, initHeight);
|
const QSize targetSize(initWidth, initHeight);
|
||||||
pixmap = pixmap.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
image = image.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||||
|
|
||||||
// Crop to center
|
// Crop to center
|
||||||
int x = (pixmap.width() - targetSize.width()) / 2;
|
int x = (image.width() - targetSize.width()) / 2;
|
||||||
int y = (pixmap.height() - targetSize.height()) / 2;
|
int y = (image.height() - targetSize.height()) / 2;
|
||||||
pixmap = pixmap.copy(x, y, targetSize.width(), targetSize.height());
|
image = image.copy(x, y, targetSize.width(), targetSize.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::focusNextImage() {
|
void ImagesCarousel::focusNextImage() {
|
||||||
@@ -242,7 +222,7 @@ void ImagesCarousel::_focusCurrImage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_scrollAnimation = new QPropertyAnimation(hScrollBar, "value");
|
m_scrollAnimation = new QPropertyAnimation(hScrollBar, "value");
|
||||||
m_scrollAnimation->setDuration(300);
|
m_scrollAnimation->setDuration(s_animationDuration);
|
||||||
m_scrollAnimation->setStartValue(hScrollBar->value());
|
m_scrollAnimation->setStartValue(hScrollBar->value());
|
||||||
m_scrollAnimation->setEndValue(leftOffset);
|
m_scrollAnimation->setEndValue(leftOffset);
|
||||||
m_scrollAnimation->setEasingCurve(QEasingCurve::OutCubic);
|
m_scrollAnimation->setEasingCurve(QEasingCurve::OutCubic);
|
||||||
@@ -282,6 +262,13 @@ void ImagesCarousel::_onScrollBarValueChanged(int value) {
|
|||||||
_focusCurrImage();
|
_focusCurrImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImagesCarousel::_onItemClicked(int index) {
|
||||||
|
// if (m_suppressAutoFocus) return;
|
||||||
|
_unfocusCurrImage();
|
||||||
|
m_currentIndex = index;
|
||||||
|
_focusCurrImage();
|
||||||
|
}
|
||||||
|
|
||||||
ImageItem::ImageItem(const ImageData* data,
|
ImageItem::ImageItem(const ImageData* data,
|
||||||
const int itemWidth,
|
const int itemWidth,
|
||||||
const int itemHeight,
|
const int itemHeight,
|
||||||
@@ -293,7 +280,7 @@ ImageItem::ImageItem(const ImageData* data,
|
|||||||
m_itemSize(itemWidth, itemHeight),
|
m_itemSize(itemWidth, itemHeight),
|
||||||
m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
|
m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
|
||||||
setScaledContents(true);
|
setScaledContents(true);
|
||||||
setPixmap(data->pixmap);
|
setPixmap(QPixmap::fromImage(data->image));
|
||||||
setFixedSize(itemWidth, itemHeight);
|
setFixedSize(itemWidth, itemHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 01:22:53
|
* @Date: 2025-08-05 01:22:53
|
||||||
* @LastEditTime: 2025-08-06 22:40:18
|
* @LastEditTime: 2025-08-07 00:27:03
|
||||||
* @Description: Animated carousel widget for displaying and selecting images.
|
* @Description: Animated carousel widget for displaying and selecting images.
|
||||||
*/
|
*/
|
||||||
#ifndef IMAGES_CAROUSEL_H
|
#ifndef IMAGES_CAROUSEL_H
|
||||||
#define IMAGES_CAROUSEL_H
|
#define IMAGES_CAROUSEL_H
|
||||||
|
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
@@ -36,7 +38,7 @@ class ImagesCarouselScrollArea;
|
|||||||
*/
|
*/
|
||||||
struct ImageData {
|
struct ImageData {
|
||||||
QFileInfo file;
|
QFileInfo file;
|
||||||
QPixmap pixmap;
|
QImage image;
|
||||||
|
|
||||||
explicit ImageData(const QString& p, const int initWidth, const int initHeight);
|
explicit ImageData(const QString& p, const int initWidth, const int initHeight);
|
||||||
};
|
};
|
||||||
@@ -63,7 +65,7 @@ class ImageItem : public QLabel {
|
|||||||
|
|
||||||
[[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); }
|
[[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); }
|
||||||
|
|
||||||
[[nodiscard]] const QPixmap& getPixmap() const { return m_data->pixmap; }
|
[[nodiscard]] const QImage& getThumbnail() const { return m_data->image; }
|
||||||
|
|
||||||
[[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); }
|
[[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); }
|
||||||
|
|
||||||
@@ -117,6 +119,7 @@ class ImagesCarousel : public QWidget {
|
|||||||
QWidget* parent = nullptr);
|
QWidget* parent = nullptr);
|
||||||
~ImagesCarousel();
|
~ImagesCarousel();
|
||||||
|
|
||||||
|
static constexpr int s_debounceInterval = 200;
|
||||||
static constexpr int s_animationDuration = 300;
|
static constexpr int s_animationDuration = 300;
|
||||||
|
|
||||||
[[nodiscard]] QString getCurrentImagePath() const {
|
[[nodiscard]] QString getCurrentImagePath() const {
|
||||||
@@ -140,22 +143,19 @@ class ImagesCarousel : public QWidget {
|
|||||||
private slots:
|
private slots:
|
||||||
void _unfocusCurrImage();
|
void _unfocusCurrImage();
|
||||||
void _onScrollBarValueChanged(int value);
|
void _onScrollBarValueChanged(int value);
|
||||||
|
void _onItemClicked(int index);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
appendImages(const QStringList& paths);
|
appendImages(const QStringList& paths);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_INVOKABLE void _addImageToQueue(const ImageData* data);
|
|
||||||
void _focusCurrImage();
|
void _focusCurrImage();
|
||||||
void _updateImages();
|
Q_INVOKABLE void _insertImage(const ImageData* item);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ImagesCarousel* ui;
|
Ui::ImagesCarousel* ui;
|
||||||
QMutex m_queueMutex;
|
|
||||||
QQueue<ImageItem*> m_imageQueue;
|
|
||||||
QList<ImageItem*> m_loadedImages;
|
QList<ImageItem*> m_loadedImages;
|
||||||
QTimer* m_updateTimer;
|
|
||||||
int m_currentIndex = 0;
|
int m_currentIndex = 0;
|
||||||
QPropertyAnimation* m_scrollAnimation = nullptr;
|
QPropertyAnimation* m_scrollAnimation = nullptr;
|
||||||
QHBoxLayout* m_imagesLayout = nullptr;
|
QHBoxLayout* m_imagesLayout = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user