From b4988f449918a499470d322361763fe222be885c Mon Sep 17 00:00:00 2001 From: Uyanide Date: Wed, 6 Aug 2025 01:35:04 +0200 Subject: [PATCH] feat: auto focusing when scrolling with mouse (which should have a second scroll wheel like mine:) or keyboard --- config.example.json | 2 +- src/designer/main_window.ui | 2 +- src/images_carousel.cpp | 76 +++++++++++++++++++++++++++++++++---- src/images_carousel.h | 27 ++++++++----- 4 files changed, 88 insertions(+), 19 deletions(-) diff --git a/config.example.json b/config.example.json index eb29998..52c8471 100644 --- a/config.example.json +++ b/config.example.json @@ -23,7 +23,7 @@ "window_height": 500 }, "sort": { - "type": "size", + "type": "name", "reverse": false } } \ No newline at end of file diff --git a/src/designer/main_window.ui b/src/designer/main_window.ui index 9e38a54..2e02358 100644 --- a/src/designer/main_window.ui +++ b/src/designer/main_window.ui @@ -47,7 +47,7 @@ 20 - 20 + 10 diff --git a/src/images_carousel.cpp b/src/images_carousel.cpp index 7dbca19..5954125 100644 --- a/src/images_carousel.cpp +++ b/src/images_carousel.cpp @@ -1,13 +1,15 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @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. */ #include "images_carousel.h" #include #include +#include +#include #include #include @@ -30,7 +32,6 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio, : QWidget(parent), ui(new Ui::ImagesCarousel), m_updateTimer(new QTimer(this)), - m_scrollAnimation(nullptr), m_itemWidth(itemWidth), m_itemHeight(static_cast(itemWidth / itemAspectRatio)), m_itemFocusWidth(itemFocusWidth), @@ -40,12 +41,41 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio, ui->setupUi(this); m_imagesLayout = dynamic_cast(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); - connect(this, &ImagesCarousel::imagesLoaded, this, [this]() { - _focusCurrImage(); - }); + // Auto focus when scrolling + 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() { @@ -57,7 +87,7 @@ ImagesCarousel::~ImagesCarousel() { // ... if (m_scrollAnimation) { m_scrollAnimation->stop(); - delete m_scrollAnimation; + m_scrollAnimation->deleteLater(); } } @@ -199,7 +229,7 @@ void ImagesCarousel::_focusCurrImage() { if (m_scrollAnimation) { m_scrollAnimation->stop(); - delete m_scrollAnimation; + m_scrollAnimation->deleteLater(); m_scrollAnimation = nullptr; } @@ -208,9 +238,39 @@ void ImagesCarousel::_focusCurrImage() { m_scrollAnimation->setStartValue(hScrollBar->value()); m_scrollAnimation->setEndValue(leftOffset); 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(); } +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, const int itemWidth, const int itemHeight, diff --git a/src/images_carousel.h b/src/images_carousel.h index 06ef5ae..4c53f79 100644 --- a/src/images_carousel.h +++ b/src/images_carousel.h @@ -1,7 +1,7 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @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. */ #ifndef IMAGES_CAROUSEL_H @@ -125,9 +125,11 @@ class ImagesCarousel : public QWidget { private slots: void _unfocusCurrImage(); + void _onScrollBarValueChanged(int value); public: - void appendImages(const QStringList& paths); + void + appendImages(const QStringList& paths); private: Q_INVOKABLE void _addImageToQueue(const ImageData* data); @@ -140,11 +142,14 @@ class ImagesCarousel : public QWidget { QQueue m_imageQueue; QList m_loadedImages; QTimer* m_updateTimer; - int m_currentIndex = 0; - QPropertyAnimation* m_scrollAnimation; - QHBoxLayout* m_imagesLayout = nullptr; + int m_currentIndex = 0; + QPropertyAnimation* m_scrollAnimation = nullptr; + QHBoxLayout* m_imagesLayout = nullptr; QMutex m_imageCountMutex; - int m_imageCount = 0; + int m_imageCount = 0; + bool m_suppressAutoFocus = false; + int m_pendingScrollValue = 0; + QTimer* m_scrollDebounceTimer = nullptr; signals: void imageFocused(const QString& path, const int index, const int count); @@ -157,13 +162,17 @@ class ImagesCarouselScrollArea : public QScrollArea { public: explicit ImagesCarouselScrollArea(QWidget* parent = nullptr) : QScrollArea(parent) { - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setWidgetResizable(true); + // setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + // setWidgetResizable(true); } protected: void keyPressEvent(QKeyEvent* event) override { - event->ignore(); + if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) { + event->ignore(); + } else { + QScrollArea::keyPressEvent(event); + } } };