diff --git a/modules/sidebar/NotifDockList.qml b/modules/sidebar/NotifDockList.qml index 8517870b..98ad0d4d 100644 --- a/modules/sidebar/NotifDockList.qml +++ b/modules/sidebar/NotifDockList.qml @@ -73,6 +73,7 @@ LazyListView { } LazyListView.preferredHeight: closed ? 0 : notifInner.nonAnimHeight + LazyListView.visibleHeight: notifInner.implicitHeight implicitHeight: notifInner.implicitHeight opacity: LazyListView.removing || closed || LazyListView.adding ? 0 : 1 diff --git a/plugin/src/Caelestia/Components/lazylistview.cpp b/plugin/src/Caelestia/Components/lazylistview.cpp index c92f6963..84073315 100644 --- a/plugin/src/Caelestia/Components/lazylistview.cpp +++ b/plugin/src/Caelestia/Components/lazylistview.cpp @@ -22,6 +22,17 @@ void LazyListViewAttached::setPreferredHeight(qreal height) { emit preferredHeightChanged(); } +qreal LazyListViewAttached::visibleHeight() const { + return m_visibleHeight; +} + +void LazyListViewAttached::setVisibleHeight(qreal height) { + if (qFuzzyCompare(m_visibleHeight, height)) + return; + m_visibleHeight = height; + emit visibleHeightChanged(); +} + bool LazyListViewAttached::adding() const { return m_adding; } @@ -210,6 +221,22 @@ qreal LazyListView::delegateHeight(QQuickItem* item) { return item->implicitHeight(); } +qreal LazyListView::delegateVisibleHeight(QQuickItem* item) { + if (!item) + return 0; + + auto* attached = qobject_cast( + qmlAttachedPropertiesObject(item, false)); + if (attached) { + if (attached->visibleHeight() >= 0) + return attached->visibleHeight(); + if (attached->preferredHeight() >= 0) + return attached->preferredHeight(); + } + + return item->implicitHeight(); +} + // --- Add Animation --- int LazyListView::addDuration() const { @@ -388,15 +415,33 @@ void LazyListView::updatePolish() { // --- Layout Engine --- void LazyListView::relayout() { + // Layout positioning uses preferredHeight (final/non-animated) qreal y = 0; for (auto& record : m_layout) { record.targetY = y; y += (record.heightKnown ? record.height : effectiveEstimatedHeight()) + m_spacing; } - const qreal newHeight = m_layout.isEmpty() ? 0 : y - m_spacing; - if (!qFuzzyCompare(m_contentHeight, newHeight)) { - m_contentHeight = newHeight; + // Content height tracks actual visible heights so scrolling follows animations + qreal visY = 0; + for (int i = 0; i < static_cast(m_layout.size()); ++i) { + qreal h; + if (m_delegates.contains(i) && m_delegates[i].item) + h = delegateVisibleHeight(m_delegates[i].item); + else + h = m_layout[i].heightKnown ? m_layout[i].height : effectiveEstimatedHeight(); + visY += h + m_spacing; + } + qreal maxBottom = m_layout.isEmpty() ? 0 : visY - m_spacing; + + // Account for dying delegates still visually present + for (const auto& dying : m_dyingDelegates) { + if (dying.item) + maxBottom = std::max(maxBottom, dying.item->y() + delegateVisibleHeight(dying.item)); + } + + if (!qFuzzyCompare(m_contentHeight, maxBottom)) { + m_contentHeight = maxBottom; emit contentHeightChanged(); } } @@ -591,12 +636,14 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) { // Watch implicitHeight as fallback connect(entry.item, &QQuickItem::implicitHeightChanged, this, onHeightChanged); - // Watch attached preferredHeight if the delegate uses it + // Watch attached properties if the delegate uses them auto* attached = qobject_cast( qmlAttachedPropertiesObject(entry.item, false)); if (attached) { entry.attachedConnection = connect(attached, &LazyListViewAttached::preferredHeightChanged, this, onHeightChanged); + connect(attached, &LazyListViewAttached::visibleHeightChanged, + this, [this] { polish(); }); } return entry; diff --git a/plugin/src/Caelestia/Components/lazylistview.hpp b/plugin/src/Caelestia/Components/lazylistview.hpp index b934a2f4..d2cc4fe6 100644 --- a/plugin/src/Caelestia/Components/lazylistview.hpp +++ b/plugin/src/Caelestia/Components/lazylistview.hpp @@ -18,6 +18,7 @@ class LazyListViewAttached : public QObject { Q_OBJECT Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged) + Q_PROPERTY(qreal visibleHeight READ visibleHeight WRITE setVisibleHeight NOTIFY visibleHeightChanged) Q_PROPERTY(bool adding READ adding NOTIFY addingChanged) Q_PROPERTY(bool removing READ removing NOTIFY removingChanged) @@ -27,6 +28,9 @@ public: [[nodiscard]] qreal preferredHeight() const; void setPreferredHeight(qreal height); + [[nodiscard]] qreal visibleHeight() const; + void setVisibleHeight(qreal height); + [[nodiscard]] bool adding() const; void setAdding(bool adding); @@ -35,11 +39,13 @@ public: signals: void preferredHeightChanged(); + void visibleHeightChanged(); void addingChanged(); void removingChanged(); private: qreal m_preferredHeight = -1; + qreal m_visibleHeight = -1; bool m_adding = false; bool m_removing = false; }; @@ -209,6 +215,7 @@ private: [[nodiscard]] QRectF effectiveViewport() const; [[nodiscard]] qreal effectiveEstimatedHeight() const; [[nodiscard]] static qreal delegateHeight(QQuickItem* item); + [[nodiscard]] static qreal delegateVisibleHeight(QQuickItem* item); void trackHeight(qreal height); void untrackHeight(qreal height);