fix: notif list viewport jumping around on delegate creation

This commit is contained in:
2 * r + 2 * t 2026-04-09 19:52:54 +10:00
parent 82f177fa3f
commit 8fbf85da86
3 changed files with 42 additions and 0 deletions

View file

@ -22,6 +22,7 @@ LazyListView {
cacheBuffer: 400
asynchronous: true
onViewportAdjustNeeded: d => container.contentY += d
useCustomViewport: true
viewport: Qt.rect(0, container.contentY, width, container.height)
@ -66,6 +67,7 @@ LazyListView {
clearTimer.start();
}
LazyListView.trackViewport: notifInner.expanded || notifInner.nonAnimHeight < notifInner.implicitHeight
LazyListView.preferredHeight: closed ? 0 : notifInner.nonAnimHeight
LazyListView.visibleHeight: notifInner.implicitHeight
implicitHeight: notifInner.implicitHeight

View file

@ -62,6 +62,17 @@ void LazyListViewAttached::setRemoving(bool removing) {
emit removingChanged();
}
bool LazyListViewAttached::trackViewport() const {
return m_trackViewport;
}
void LazyListViewAttached::setTrackViewport(bool track) {
if (m_trackViewport == track)
return;
m_trackViewport = track;
emit trackViewportChanged();
}
// --- LazyListView ---
LazyListView::LazyListView(QQuickItem* parent)
@ -628,11 +639,21 @@ void LazyListView::syncDelegates() {
if (entry.item) {
const qreal h = delegateHeight(entry.item);
if (!m_layout[i].heightKnown || !qFuzzyCompare(m_layout[i].height + 1.0, h + 1.0)) {
const qreal oldLayoutH = m_layout[i].heightKnown ? m_layout[i].height : effectiveEstimatedHeight();
if (m_layout[i].heightKnown)
untrackHeight(m_layout[i].height);
m_layout[i].height = h;
m_layout[i].heightKnown = true;
trackHeight(h);
// Compensate if tracked item materializes above viewport
auto* att =
qobject_cast<LazyListViewAttached*>(qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
if (att && att->trackViewport()) {
const qreal vpTop = m_useCustomViewport ? m_viewport.y() : m_contentY;
if (m_layout[i].targetY < vpTop)
emit viewportAdjustNeeded(h - oldLayoutH);
}
layoutChanged = true;
}
entry.item->setY(m_layout[i].targetY - m_contentY);
@ -745,6 +766,18 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
if (wasKnown)
untrackHeight(oldH);
trackHeight(h);
// If this tracked item is above the viewport, emit a
// compensation delta so the consumer can adjust scroll.
if (wasKnown) {
auto* att = qobject_cast<LazyListViewAttached*>(qmlAttachedPropertiesObject<LazyListView>(item, false));
if (att && att->trackViewport()) {
const qreal vpTop = m_useCustomViewport ? m_viewport.y() : m_contentY;
if (m_layout[idx].targetY < vpTop)
emit viewportAdjustNeeded(h - oldH);
}
}
if (!m_relayoutPending) {
m_relayoutPending = true;
QTimer::singleShot(0, this, [this] {

View file

@ -21,6 +21,7 @@ class LazyListViewAttached : public QObject {
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)
Q_PROPERTY(bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY trackViewportChanged)
public:
explicit LazyListViewAttached(QObject* parent = nullptr);
@ -37,17 +38,22 @@ public:
[[nodiscard]] bool removing() const;
void setRemoving(bool removing);
[[nodiscard]] bool trackViewport() const;
void setTrackViewport(bool track);
signals:
void preferredHeightChanged();
void visibleHeightChanged();
void addingChanged();
void removingChanged();
void trackViewportChanged();
private:
qreal m_preferredHeight = -1;
qreal m_visibleHeight = -1;
bool m_adding = false;
bool m_removing = false;
bool m_trackViewport = false;
};
class LazyListView : public QQuickItem {
@ -198,6 +204,7 @@ signals:
void moveCurveChanged();
void countChanged();
void settledChanged();
void viewportAdjustNeeded(qreal delta);
protected:
void componentComplete() override;