feat: auto focusing when scrolling with mouse (which should have a second scroll wheel like mine:) or keyboard
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@
|
|||||||
"window_height": 500
|
"window_height": 500
|
||||||
},
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
"type": "size",
|
"type": "name",
|
||||||
"reverse": false
|
"reverse": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>20</height>
|
<height>10</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
|
|||||||
+68
-8
@@ -1,13 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* @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 00:47:21
|
* @LastEditTime: 2025-08-06 01:27:49
|
||||||
* @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"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <qboxlayout.h>
|
#include <qboxlayout.h>
|
||||||
|
#include <qdebug.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
@@ -30,7 +32,6 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
|
|||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
ui(new Ui::ImagesCarousel),
|
ui(new Ui::ImagesCarousel),
|
||||||
m_updateTimer(new QTimer(this)),
|
m_updateTimer(new QTimer(this)),
|
||||||
m_scrollAnimation(nullptr),
|
|
||||||
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),
|
||||||
@@ -40,12 +41,41 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
m_imagesLayout = dynamic_cast<QHBoxLayout*>(ui->scrollAreaWidgetContents->layout());
|
m_imagesLayout = dynamic_cast<QHBoxLayout*>(ui->scrollAreaWidgetContents->layout());
|
||||||
connect(m_updateTimer, &QTimer::timeout, this, &ImagesCarousel::_updateImages);
|
|
||||||
|
// Load initial images
|
||||||
|
connect(m_updateTimer,
|
||||||
|
&QTimer::timeout,
|
||||||
|
this,
|
||||||
|
&ImagesCarousel::_updateImages);
|
||||||
|
connect(this,
|
||||||
|
&ImagesCarousel::imagesLoaded,
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
_focusCurrImage();
|
||||||
|
disconnect(this, &ImagesCarousel::imagesLoaded, this, nullptr);
|
||||||
|
});
|
||||||
m_updateTimer->start(100);
|
m_updateTimer->start(100);
|
||||||
|
|
||||||
connect(this, &ImagesCarousel::imagesLoaded, this, [this]() {
|
// Auto focus when scrolling
|
||||||
_focusCurrImage();
|
m_scrollDebounceTimer = new QTimer(this);
|
||||||
});
|
m_scrollDebounceTimer->setSingleShot(true);
|
||||||
|
m_scrollDebounceTimer->setInterval(200);
|
||||||
|
connect(m_scrollDebounceTimer,
|
||||||
|
&QTimer::timeout,
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
_onScrollBarValueChanged(m_pendingScrollValue);
|
||||||
|
});
|
||||||
|
connect(ui->scrollArea->horizontalScrollBar(),
|
||||||
|
&QScrollBar::valueChanged,
|
||||||
|
this,
|
||||||
|
[this](int value) {
|
||||||
|
m_pendingScrollValue = value;
|
||||||
|
if (m_suppressAutoFocus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_scrollDebounceTimer->start();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ImagesCarousel::~ImagesCarousel() {
|
ImagesCarousel::~ImagesCarousel() {
|
||||||
@@ -57,7 +87,7 @@ ImagesCarousel::~ImagesCarousel() {
|
|||||||
// ...
|
// ...
|
||||||
if (m_scrollAnimation) {
|
if (m_scrollAnimation) {
|
||||||
m_scrollAnimation->stop();
|
m_scrollAnimation->stop();
|
||||||
delete m_scrollAnimation;
|
m_scrollAnimation->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +229,7 @@ void ImagesCarousel::_focusCurrImage() {
|
|||||||
|
|
||||||
if (m_scrollAnimation) {
|
if (m_scrollAnimation) {
|
||||||
m_scrollAnimation->stop();
|
m_scrollAnimation->stop();
|
||||||
delete m_scrollAnimation;
|
m_scrollAnimation->deleteLater();
|
||||||
m_scrollAnimation = nullptr;
|
m_scrollAnimation = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,9 +238,39 @@ void ImagesCarousel::_focusCurrImage() {
|
|||||||
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);
|
||||||
|
|
||||||
|
// Suppress auto focus during animation
|
||||||
|
connect(m_scrollAnimation,
|
||||||
|
&QPropertyAnimation::finished,
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
m_suppressAutoFocus = false;
|
||||||
|
});
|
||||||
|
m_suppressAutoFocus = true;
|
||||||
m_scrollAnimation->start();
|
m_scrollAnimation->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImagesCarousel::_onScrollBarValueChanged(int value) {
|
||||||
|
// Stop the animation if it is running
|
||||||
|
if (m_scrollAnimation && m_scrollAnimation->state() == QPropertyAnimation::Running) {
|
||||||
|
m_scrollAnimation->stop();
|
||||||
|
m_scrollAnimation->deleteLater();
|
||||||
|
m_scrollAnimation = nullptr;
|
||||||
|
}
|
||||||
|
int centerOffset = (value + m_itemFocusWidth / 2);
|
||||||
|
int itemOffset = m_itemWidth + ui->scrollAreaWidgetContents->layout()->spacing();
|
||||||
|
int index = centerOffset / itemOffset;
|
||||||
|
if (index < 0 || index >= m_loadedImages.size()) {
|
||||||
|
return; // Out of bounds
|
||||||
|
}
|
||||||
|
if (index == m_currentIndex) {
|
||||||
|
return; // Already focused
|
||||||
|
}
|
||||||
|
_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,
|
||||||
|
|||||||
+18
-9
@@ -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 00:47:12
|
* @LastEditTime: 2025-08-06 01:32:56
|
||||||
* @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
|
||||||
@@ -125,9 +125,11 @@ class ImagesCarousel : public QWidget {
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void _unfocusCurrImage();
|
void _unfocusCurrImage();
|
||||||
|
void _onScrollBarValueChanged(int value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void appendImages(const QStringList& paths);
|
void
|
||||||
|
appendImages(const QStringList& paths);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_INVOKABLE void _addImageToQueue(const ImageData* data);
|
Q_INVOKABLE void _addImageToQueue(const ImageData* data);
|
||||||
@@ -140,11 +142,14 @@ class ImagesCarousel : public QWidget {
|
|||||||
QQueue<ImageItem*> m_imageQueue;
|
QQueue<ImageItem*> m_imageQueue;
|
||||||
QList<ImageItem*> m_loadedImages;
|
QList<ImageItem*> m_loadedImages;
|
||||||
QTimer* m_updateTimer;
|
QTimer* m_updateTimer;
|
||||||
int m_currentIndex = 0;
|
int m_currentIndex = 0;
|
||||||
QPropertyAnimation* m_scrollAnimation;
|
QPropertyAnimation* m_scrollAnimation = nullptr;
|
||||||
QHBoxLayout* m_imagesLayout = nullptr;
|
QHBoxLayout* m_imagesLayout = nullptr;
|
||||||
QMutex m_imageCountMutex;
|
QMutex m_imageCountMutex;
|
||||||
int m_imageCount = 0;
|
int m_imageCount = 0;
|
||||||
|
bool m_suppressAutoFocus = false;
|
||||||
|
int m_pendingScrollValue = 0;
|
||||||
|
QTimer* m_scrollDebounceTimer = nullptr;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void imageFocused(const QString& path, const int index, const int count);
|
void imageFocused(const QString& path, const int index, const int count);
|
||||||
@@ -157,13 +162,17 @@ class ImagesCarouselScrollArea : public QScrollArea {
|
|||||||
public:
|
public:
|
||||||
explicit ImagesCarouselScrollArea(QWidget* parent = nullptr)
|
explicit ImagesCarouselScrollArea(QWidget* parent = nullptr)
|
||||||
: QScrollArea(parent) {
|
: QScrollArea(parent) {
|
||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
// setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
setWidgetResizable(true);
|
// setWidgetResizable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent* event) override {
|
void keyPressEvent(QKeyEvent* event) override {
|
||||||
event->ignore();
|
if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
|
||||||
|
event->ignore();
|
||||||
|
} else {
|
||||||
|
QScrollArea::keyPressEvent(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user