From b566096b1fde2c58974427d9d3d9ff61b5e2b6a0 Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Wed, 25 Jun 2025 20:55:42 +0800 Subject: [PATCH] feat: option to disable gallery looping This change is sponsored by @superuser7777 Related: https://github.com/BLumia/pineapple-pictures/issues/153 --- app/mainwindow.cpp | 20 ++++++++++++++------ app/mainwindow.h | 1 + app/playlistmanager.cpp | 14 ++++++++++++-- app/playlistmanager.h | 4 +++- app/settings.cpp | 11 +++++++++++ app/settings.h | 2 ++ app/settingsdialog.cpp | 21 ++++++++++++++++++--- app/settingsdialog.h | 1 + 8 files changed, 62 insertions(+), 12 deletions(-) diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index d4688a5..9cf99ad 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -139,10 +139,7 @@ MainWindow::MainWindow(QWidget *parent) m_gv->setOpacity(0, false); m_closeButton->setOpacity(0, false); - connect(m_pm, &PlaylistManager::totalCountChanged, this, [this](int galleryFileCount) { - m_prevButton->setVisible(galleryFileCount > 1); - m_nextButton->setVisible(galleryFileCount > 1); - }); + connect(m_pm, &PlaylistManager::totalCountChanged, this, &MainWindow::updateGalleryButtonsVisibility); connect(m_pm->model(), &PlaylistModel::modelReset, this, std::bind(&MainWindow::galleryCurrent, this, false, false)); connect(m_pm, &PlaylistManager::currentIndexChanged, this, std::bind(&MainWindow::galleryCurrent, this, true, false)); @@ -279,6 +276,8 @@ void MainWindow::galleryCurrent(bool showLoadImageHintWhenEmpty, bool reloadImag m_graphicsView->showText(QCoreApplication::translate("GraphicsScene", "Drag image here")); } + updateGalleryButtonsVisibility(); + if (shouldResetfileWatcher) updateFileWatcher(); } @@ -603,8 +602,7 @@ void MainWindow::toggleProtectedMode() { m_protectedMode = !m_protectedMode; m_closeButton->setVisible(!m_protectedMode); - m_prevButton->setVisible(!m_protectedMode); - m_nextButton->setVisible(!m_protectedMode); + updateGalleryButtonsVisibility(); } void MainWindow::toggleStayOnTop() @@ -927,3 +925,13 @@ bool MainWindow::updateFileWatcher(const QString &basePath) if (!basePath.isEmpty()) return m_fileSystemWatcher->addPath(basePath); return false; } + +void MainWindow::updateGalleryButtonsVisibility() +{ + const int galleryFileCount = m_pm->totalCount(); + const bool loopGallery = Settings::instance()->loopGallery(); + m_prevButton->setVisible(!m_protectedMode && galleryFileCount > 1); + m_nextButton->setVisible(!m_protectedMode && galleryFileCount > 1); + m_prevButton->setEnabled(loopGallery || !m_pm->isFirstIndex()); + m_nextButton->setEnabled(loopGallery || !m_pm->isLastIndex()); +} diff --git a/app/mainwindow.h b/app/mainwindow.h index 4116dde..7de8122 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -110,6 +110,7 @@ private slots: private: bool updateFileWatcher(const QString & basePath = QString()); + void updateGalleryButtonsVisibility(); private: ActionManager *m_am; diff --git a/app/playlistmanager.cpp b/app/playlistmanager.cpp index 42cbf3e..7ada680 100644 --- a/app/playlistmanager.cpp +++ b/app/playlistmanager.cpp @@ -196,7 +196,7 @@ QModelIndex PlaylistManager::previousIndex() const int count = totalCount(); if (count == 0) return {}; - return m_model.index(m_currentIndex - 1 < 0 ? count - 1 : m_currentIndex - 1); + return m_model.index(isFirstIndex() ? count - 1 : m_currentIndex - 1); } QModelIndex PlaylistManager::nextIndex() const @@ -204,7 +204,7 @@ QModelIndex PlaylistManager::nextIndex() const int count = totalCount(); if (count == 0) return {}; - return m_model.index(m_currentIndex + 1 == count ? 0 : m_currentIndex + 1); + return m_model.index(isLastIndex() ? 0 : m_currentIndex + 1); } QModelIndex PlaylistManager::curIndex() const @@ -212,6 +212,16 @@ QModelIndex PlaylistManager::curIndex() const return m_model.index(m_currentIndex); } +bool PlaylistManager::isFirstIndex() const +{ + return m_currentIndex == 0; +} + +bool PlaylistManager::isLastIndex() const +{ + return m_currentIndex + 1 == totalCount(); +} + void PlaylistManager::setCurrentIndex(const QModelIndex &index) { if (index.isValid() && index.row() >= 0 && index.row() < totalCount()) { diff --git a/app/playlistmanager.h b/app/playlistmanager.h index ae4640a..0314987 100644 --- a/app/playlistmanager.h +++ b/app/playlistmanager.h @@ -62,10 +62,12 @@ public: Q_INVOKABLE QModelIndex loadPlaylist(const QList & urls); Q_INVOKABLE QModelIndex loadPlaylist(const QUrl & url); - int totalCount() const; + inline int totalCount() const; QModelIndex previousIndex() const; QModelIndex nextIndex() const; QModelIndex curIndex() const; + inline bool isFirstIndex() const; + inline bool isLastIndex() const; void setCurrentIndex(const QModelIndex & index); QUrl urlByIndex(const QModelIndex & index); QString localFileByIndex(const QModelIndex & index); diff --git a/app/settings.cpp b/app/settings.cpp index b3ef88b..a63998b 100644 --- a/app/settings.cpp +++ b/app/settings.cpp @@ -60,6 +60,11 @@ bool Settings::useLightCheckerboard() const return m_qsettings->value("use_light_checkerboard", false).toBool(); } +bool Settings::loopGallery() const +{ + return m_qsettings->value("loop_gallery", true).toBool(); +} + Settings::DoubleClickBehavior Settings::doubleClickBehavior() const { QString result = m_qsettings->value("double_click_behavior", "Close").toString(); @@ -106,6 +111,12 @@ void Settings::setUseLightCheckerboard(bool light) m_qsettings->sync(); } +void Settings::setLoopGallery(bool on) +{ + m_qsettings->setValue("loop_gallery", on); + m_qsettings->sync(); +} + void Settings::setDoubleClickBehavior(DoubleClickBehavior dcb) { m_qsettings->setValue("double_click_behavior", QEnumHelper::toString(dcb)); diff --git a/app/settings.h b/app/settings.h index 0ac66c5..80c4cd0 100644 --- a/app/settings.h +++ b/app/settings.h @@ -37,6 +37,7 @@ public: bool stayOnTop() const; bool useBuiltInCloseAnimation() const; bool useLightCheckerboard() const; + bool loopGallery() const; DoubleClickBehavior doubleClickBehavior() const; MouseWheelBehavior mouseWheelBehavior() const; WindowSizeBehavior initWindowSizeBehavior() const; @@ -45,6 +46,7 @@ public: void setStayOnTop(bool on); void setUseBuiltInCloseAnimation(bool on); void setUseLightCheckerboard(bool light); + void setLoopGallery(bool on); void setDoubleClickBehavior(DoubleClickBehavior dcb); void setMouseWheelBehavior(MouseWheelBehavior mwb); void setInitWindowSizeBehavior(WindowSizeBehavior wsb); diff --git a/app/settingsdialog.cpp b/app/settingsdialog.cpp index 6f1479f..f20490b 100644 --- a/app/settingsdialog.cpp +++ b/app/settingsdialog.cpp @@ -22,6 +22,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) , m_stayOnTop(new QCheckBox) , m_useBuiltInCloseAnimation(new QCheckBox) , m_useLightCheckerboard(new QCheckBox) + , m_loopGallery(new QCheckBox) , m_doubleClickBehavior(new QComboBox) , m_mouseWheelBehavior(new QComboBox) , m_initWindowSizeBehavior(new QComboBox) @@ -121,6 +122,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) settingsForm->addRow(tr("Stay on top when start-up"), m_stayOnTop); settingsForm->addRow(tr("Use built-in close window animation"), m_useBuiltInCloseAnimation); settingsForm->addRow(tr("Use light-color checkerboard"), m_useLightCheckerboard); + settingsForm->addRow(tr("Loop the loaded gallery"), m_loopGallery); settingsForm->addRow(tr("Double-click behavior"), m_doubleClickBehavior); settingsForm->addRow(tr("Mouse wheel behavior"), m_mouseWheelBehavior); settingsForm->addRow(tr("Default window size"), m_initWindowSizeBehavior); @@ -129,6 +131,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) m_stayOnTop->setChecked(Settings::instance()->stayOnTop()); m_useBuiltInCloseAnimation->setChecked(Settings::instance()->useBuiltInCloseAnimation()); m_useLightCheckerboard->setChecked(Settings::instance()->useLightCheckerboard()); + m_loopGallery->setChecked(Settings::instance()->loopGallery()); m_doubleClickBehavior->setModel(new QStringListModel(dcbDropDown)); Settings::DoubleClickBehavior dcb = Settings::instance()->doubleClickBehavior(); m_doubleClickBehavior->setCurrentIndex(static_cast(dcb)); @@ -147,18 +150,30 @@ SettingsDialog::SettingsDialog(QWidget *parent) } } - connect(m_stayOnTop, &QCheckBox::stateChanged, this, [ = ](int state){ +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) +# define QCHECKBOX_CHECKSTATECHANGED QCheckBox::checkStateChanged +# define QT_CHECKSTATE Qt::CheckState +#else +# define QCHECKBOX_CHECKSTATECHANGED QCheckBox::stateChanged +# define QT_CHECKSTATE int +#endif // QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + + connect(m_stayOnTop, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){ Settings::instance()->setStayOnTop(state == Qt::Checked); }); - connect(m_useBuiltInCloseAnimation, &QCheckBox::stateChanged, this, [ = ](int state){ + connect(m_useBuiltInCloseAnimation, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){ Settings::instance()->setUseBuiltInCloseAnimation(state == Qt::Checked); }); - connect(m_useLightCheckerboard, &QCheckBox::stateChanged, this, [ = ](int state){ + connect(m_useLightCheckerboard, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){ Settings::instance()->setUseLightCheckerboard(state == Qt::Checked); }); + connect(m_loopGallery, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){ + Settings::instance()->setLoopGallery(state == Qt::Checked); + }); + connect(m_doubleClickBehavior, QOverload::of(&QComboBox::currentIndexChanged), this, [ = ](int index){ Settings::instance()->setDoubleClickBehavior(_dc_options.at(index).first); }); diff --git a/app/settingsdialog.h b/app/settingsdialog.h index f56f07d..9c3fc56 100644 --- a/app/settingsdialog.h +++ b/app/settingsdialog.h @@ -25,6 +25,7 @@ private: QCheckBox * m_stayOnTop = nullptr; QCheckBox * m_useBuiltInCloseAnimation = nullptr; QCheckBox * m_useLightCheckerboard = nullptr; + QCheckBox * m_loopGallery = nullptr; QComboBox * m_doubleClickBehavior = nullptr; QComboBox * m_mouseWheelBehavior = nullptr; QComboBox * m_initWindowSizeBehavior = nullptr;