Files
WallReel/src/images_carousel.h
T

216 lines
5.5 KiB
C++

/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-08-07 00:27:03
* @Description: Animated carousel widget for displaying and selecting images.
*/
#ifndef IMAGES_CAROUSEL_H
#define IMAGES_CAROUSEL_H
#include <qtmetamacros.h>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QMutex>
#include <QObject>
#include <QPixmap>
#include <QPropertyAnimation>
#include <QQueue>
#include <QRunnable>
#include <QScrollArea>
#include <QThreadPool>
#include <QTimer>
#include <QWidget>
#include "config.h"
class ImageData;
class ImageItem;
class ImageLoader;
class ImagesCarousel;
class ImagesCarouselScrollArea;
/**
* @brief Data structure to hold image information
* and can be safely created and passed between threads.
*/
struct ImageData {
QFileInfo file;
QImage image;
explicit ImageData(const QString& p, const int initWidth, const int initHeight);
};
/**
* @brief Image label that displays an image
*/
class ImageItem : public QLabel {
Q_OBJECT
public:
explicit ImageItem(const ImageData* data,
const int itemWidth,
const int itemHeight,
const int itemFocusWidth,
const int itemFocusHeight,
QWidget* parent = nullptr);
~ImageItem() override;
[[nodiscard]] QString getFileFullPath() const { return m_data->file.absoluteFilePath(); }
[[nodiscard]] QString getFileName() const { return m_data->file.fileName(); }
[[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); }
[[nodiscard]] const QImage& getThumbnail() const { return m_data->image; }
[[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); }
void setFocus(bool focus = true);
int m_index = 0;
protected:
void mousePressEvent(QMouseEvent* event) override {
emit clicked(m_index);
QLabel::mousePressEvent(event);
}
private:
const ImageData* m_data;
QSize m_itemSize;
QSize m_itemFocusSize;
QPropertyAnimation* m_scaleAnimation = nullptr;
signals:
void clicked(int index);
};
class ImageLoader : public QRunnable {
public:
ImageLoader(const QString& path, ImagesCarousel* carousel);
void run() override;
private:
QString m_path;
ImagesCarousel* m_carousel;
const int m_initWidth;
const int m_initHeight;
};
namespace Ui {
class ImagesCarousel;
}
class ImagesCarousel : public QWidget {
Q_OBJECT
friend void ImageLoader::run();
public:
explicit ImagesCarousel(const double itemAspectRatio,
const int itemWidth,
const int itemFocusWidth,
const Config::SortType sortType,
const bool sortReverse,
QWidget* parent = nullptr);
~ImagesCarousel();
static constexpr int s_debounceInterval = 200;
static constexpr int s_animationDuration = 300;
[[nodiscard]] QString getCurrentImagePath() const {
if (m_currentIndex < 0 || m_currentIndex >= m_loadedImages.size()) {
return "";
}
return m_loadedImages[m_currentIndex]->getFileFullPath();
}
const int m_itemWidth = 320;
const int m_itemHeight = 180;
const int m_itemFocusWidth = 480;
const int m_itemFocusHeight = 270;
const Config::SortType m_sortType = Config::SortType::None;
const bool m_sortReverse = false;
public slots:
void focusNextImage();
void focusPrevImage();
private slots:
void _unfocusCurrImage();
void _onScrollBarValueChanged(int value);
void _onItemClicked(int index);
public:
void
appendImages(const QStringList& paths);
private:
void _focusCurrImage();
Q_INVOKABLE void _insertImage(const ImageData* item);
private:
Ui::ImagesCarousel* ui;
QList<ImageItem*> m_loadedImages;
int m_currentIndex = 0;
QPropertyAnimation* m_scrollAnimation = nullptr;
QHBoxLayout* m_imagesLayout = nullptr;
QMutex m_imageCountMutex;
int m_imageCount = 0;
bool m_suppressAutoFocus = false;
int m_pendingScrollValue = 0;
QTimer* m_scrollDebounceTimer = nullptr;
ImagesCarouselScrollArea* m_scrollArea = nullptr;
signals:
void imageFocused(const QString& path, const int index, const int count);
void imagesLoaded();
};
class ImagesCarouselScrollArea : public QScrollArea {
Q_OBJECT
public:
explicit ImagesCarouselScrollArea(QWidget* parent = nullptr)
: QScrollArea(parent) {
// setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// setWidgetResizable(true);
}
void setBlockInput(bool block) { m_blockInput = block; }
protected:
void keyPressEvent(QKeyEvent* event) override {
if (m_blockInput) {
event->ignore();
return;
}
if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
event->ignore();
} else {
QScrollArea::keyPressEvent(event);
}
}
void wheelEvent(QWheelEvent* event) override {
if (m_blockInput) {
event->ignore();
return;
}
if (event->angleDelta().y() != 0) {
event->ignore();
} else {
QScrollArea::wheelEvent(event);
}
}
private:
bool m_blockInput = false;
};
#endif // IMAGES_CAROUSEL_H