feat: auto focusing when scrolling with mouse (which should have a second scroll wheel like mine:) or keyboard

This commit is contained in:
2025-08-06 01:35:04 +02:00
parent f026f12e4c
commit b4988f4499
4 changed files with 88 additions and 19 deletions
+1 -1
View File
@@ -23,7 +23,7 @@
"window_height": 500 "window_height": 500
}, },
"sort": { "sort": {
"type": "size", "type": "name",
"reverse": false "reverse": false
} }
} }
+1 -1
View File
@@ -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>
+67 -7
View File
@@ -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,11 +41,40 @@ 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();
}); });
} }
@@ -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,
+14 -5
View File
@@ -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);
@@ -141,10 +143,13 @@ class ImagesCarousel : public QWidget {
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 {
if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
event->ignore(); event->ignore();
} else {
QScrollArea::keyPressEvent(event);
}
} }
}; };