From 59213cf30d9bacf9bfe0210d39617729d944adc6 Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 18 Aug 2026 19:25:03 +0200 Subject: [PATCH] fix property changed signals firing multiple times --- Modules/Launcher/CarouselView.qml | 14 +-- Plugins/ZShell/Components/carouselview.cpp | 103 +++++++++++++++------ Plugins/ZShell/Components/carouselview.hpp | 14 ++- log.log | 9 ++ 4 files changed, 101 insertions(+), 39 deletions(-) create mode 100644 log.log diff --git a/Modules/Launcher/CarouselView.qml b/Modules/Launcher/CarouselView.qml index 595dfa2..5a4a65c 100755 --- a/Modules/Launcher/CarouselView.qml +++ b/Modules/Launcher/CarouselView.qml @@ -15,6 +15,10 @@ CarouselView { readonly property var values: Wallpapers.query(root.search.text.split(" ").slice(1).join(" ")) required property var visibilities + currentIndex: { + const idx = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent); + return idx >= 0 ? idx : 0; + } delegateProperties: ({ visibilities: root.visibilities }) @@ -41,13 +45,9 @@ CarouselView { } } - Component.onCompleted: { - const idx = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent); - if (idx >= 0) - jumpToIndex(idx); - } Component.onDestruction: Wallpapers.stopPreview() - onPreviewIndexChanged: (realIndex, modelData) => { - Wallpapers.preview(modelData.path); + onCurrentItemChanged: { + if (currentItem) + Wallpapers.preview(currentItem.modelData.path); } } diff --git a/Plugins/ZShell/Components/carouselview.cpp b/Plugins/ZShell/Components/carouselview.cpp index 70776fe..0a5aa66 100644 --- a/Plugins/ZShell/Components/carouselview.cpp +++ b/Plugins/ZShell/Components/carouselview.cpp @@ -1,11 +1,13 @@ #include "carouselview.hpp" +#include #include #include #include #include #include +#include namespace ZShell::components { @@ -81,6 +83,33 @@ void CarouselView::setDelegate(QQmlComponent* c) { relayout(); } +void CarouselView::notifyCurrentIndexChanged() { + if (!m_completed) return; + emit currentIndexChanged(); +} + +void CarouselView::notifyCurrentItemChanged() { + if (!m_completed) return; + emit currentItemChanged(); +} + +void CarouselView::notifyPreviewIndexChanged(int realIndex) { + if (!m_completed || realIndex < 0) return; + emit previewIndexChanged(realIndex, m_model.at(realIndex)); +} + +void CarouselView::classBegin() {} + +void CarouselView::componentComplete() { + m_completed = true; + emit currentIndexChanged(); + if (m_currentRealIndex >= 0 && m_currentRealIndex < m_model.size()) + emit previewIndexChanged( + m_currentRealIndex, m_model.at(m_currentRealIndex)); + + emit currentItemChanged(); +} + bool CarouselView::childMouseEventFilter(QQuickItem* item, QEvent* event) { Q_UNUSED(item); @@ -163,7 +192,7 @@ void CarouselView::setModel(const QVariantList& m) { if (newCount == 0) { m_currentRealIndex = -1; m_currentItem = nullptr; - emit currentItemChanged(); + notifyCurrentItemChanged(); releaseAllItems(); rebuildArrangement(); @@ -173,19 +202,23 @@ void CarouselView::setModel(const QVariantList& m) { if (!m_initializedLayout) { m_centerBlockStart = 0; - m_currentVirtualIndex = 0; - m_contentX = contentXForIndex(0); + const int startVirtual = + m_pendingCurrentIndex >= 0 + ? qBound(0, m_pendingCurrentIndex, newCount - 1) + : 0; + m_pendingCurrentIndex = -1; + + m_currentVirtualIndex = startVirtual; + m_contentX = contentXForIndex(startVirtual); m_currentRealIndex = realIndexOf(m_currentVirtualIndex); - m_previewRealIndex = m_currentRealIndex; m_initializedLayout = true; m_layoutCapacity = qMax(m_layoutCapacity, layoutCapacityForWidth(m_maxWidth)); - setImplicitWidth(targetWidthForMaxWidth(m_maxWidth)); rebuildArrangement(); @@ -320,6 +353,22 @@ void CarouselView::setDelegateProperties(const QVariantMap& v) { } void CarouselView::setCurrentIndex(int idx) { + if (m_model.isEmpty()) { + m_pendingCurrentIndex = idx; + + if (m_currentRealIndex != idx) { + m_currentRealIndex = idx; + notifyCurrentIndexChanged(); + } + + return; + } + + if (!m_completed) { + centerInstantlyOnReal(idx); + return; + } + goToIndex(idx); } @@ -336,7 +385,7 @@ void CarouselView::updateCurrentItem() { if (item == m_currentItem) return; m_currentItem = item; - emit currentItemChanged(); + notifyCurrentItemChanged(); } void CarouselView::geometryChange(const QRectF& newGeo, const QRectF& oldGeo) { @@ -676,15 +725,12 @@ void CarouselView::updateCurrentIndexFromContentX(bool emitPreview) { if (newReal != m_currentRealIndex) { m_currentRealIndex = newReal; - emit currentIndexChanged(); + notifyCurrentIndexChanged(); } if (emitPreview && newReal != m_previewRealIndex) { m_previewRealIndex = newReal; - - if (newReal >= 0) { - emit previewIndexChanged(newReal, m_model.at(newReal)); - } + notifyPreviewIndexChanged(newReal); } } @@ -1029,13 +1075,14 @@ void CarouselView::glideTo(qreal dest) { cancelAnimations(); - m_glideAnim->setDuration(m_glideDuration); + { + QSignalBlocker blocker(m_glideAnim); - m_glideAnim->setStartValue(m_contentX); - - m_glideAnim->setEndValue(dest); - - m_glideAnim->start(); + m_glideAnim->setDuration(m_glideDuration); + m_glideAnim->setStartValue(m_contentX); + m_glideAnim->setEndValue(dest); + m_glideAnim->start(); + } } void CarouselView::snapToNearest() { @@ -1078,10 +1125,6 @@ void CarouselView::decrementCurrentIndex() { glideTo(contentXForIndex(m_currentVirtualIndex - 1)); } -void CarouselView::jumpToIndex(int realIndex) { - centerInstantlyOnReal(realIndex); -} - void CarouselView::centerInstantlyOnReal(int realIndex) { const int count = m_model.size(); @@ -1115,7 +1158,7 @@ void CarouselView::centerInstantlyOnReal(int realIndex) { if (changed) emit currentIndexChanged(); - emit previewIndexChanged(realIndex, m_model.at(realIndex)); + notifyPreviewIndexChanged(realIndex); relayout(); } @@ -1219,12 +1262,16 @@ void CarouselView::mouseReleaseEvent(QMouseEvent* event) { cancelAnimations(); - m_flickAnim->setDuration( - static_cast(qBound(120, duration, 900))); - m_flickAnim->setEasingCurve(QEasingCurve::OutQuad); - m_flickAnim->setStartValue(m_contentX); - m_flickAnim->setEndValue(dest); - m_flickAnim->start(); + { + QSignalBlocker blocker(m_flickAnim); + + m_flickAnim->setDuration( + static_cast(qBound(120, duration, 900))); + m_flickAnim->setEasingCurve(QEasingCurve::OutQuad); + m_flickAnim->setStartValue(m_contentX); + m_flickAnim->setEndValue(dest); + m_flickAnim->start(); + } event->accept(); } diff --git a/Plugins/ZShell/Components/carouselview.hpp b/Plugins/ZShell/Components/carouselview.hpp index 3f98b35..12564e6 100644 --- a/Plugins/ZShell/Components/carouselview.hpp +++ b/Plugins/ZShell/Components/carouselview.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -100,8 +101,6 @@ class CarouselView : public QQuickItem { Q_INVOKABLE void incrementCurrentIndex(); Q_INVOKABLE void decrementCurrentIndex(); - Q_INVOKABLE void jumpToIndex(int realIndex); - signals: void delegateChanged(); void modelChanged(); @@ -120,10 +119,10 @@ class CarouselView : public QQuickItem { void previewIndexChanged(int realIndex, QVariant modelData); protected: + void classBegin() override; + void componentComplete() override; bool childMouseEventFilter(QQuickItem* item, QEvent* event) override; - void geometryChange(const QRectF& newGeo, const QRectF& oldGeo) override; - void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; @@ -200,6 +199,10 @@ class CarouselView : public QQuickItem { void addVelocitySample(qint64 t, qreal x); qreal sampledVelocity() const; + void notifyCurrentIndexChanged(); + void notifyCurrentItemChanged(); + void notifyPreviewIndexChanged(int realIndex); + QQmlComponent* m_delegate = nullptr; QVariantList m_model; QVariantMap m_delegateProperties; @@ -242,6 +245,9 @@ class CarouselView : public QQuickItem { QVariantAnimation* m_flickAnim = nullptr; + bool m_completed = false; + int m_pendingCurrentIndex = -1; + bool m_initializedLayout = false; bool m_relayoutQueued = false; }; diff --git a/log.log b/log.log new file mode 100644 index 0000000..cf43b37 --- /dev/null +++ b/log.log @@ -0,0 +1,9 @@ + INFO: Launching config: "/home/zach/GitProjects/z-bar-qt/shell.qml" + INFO: Shell ID: "bd36f3a4d7a2d3b0ea8e340a90ef2957" Path ID "bd36f3a4d7a2d3b0ea8e340a90ef2957" + INFO: Saving logs to "/run/user/1000/quickshell/by-id/4tpcex6zjt/log.qslog" + INFO: QML tooling support enabled + WARN: Non list data QVariant(QObject*, 0x7f12c3a4a5c0) assigned to Variants.model, Ignoring. + WARN scene: @Modules/Polkit/Polkit.qml[133:8]: Unable to assign [undefined] to QString + WARN scene: @Modules/Polkit/Polkit.qml[171:8]: Unable to assign [undefined] to bool + WARN scene: @Helpers/Battery.qml[19:-1]: TypeError: Type error + INFO: Configuration Loaded