feat: map listview delegates to indexes
This commit is contained in:
parent
1c09222dd7
commit
8f3f1b9056
2 changed files with 42 additions and 31 deletions
|
|
@ -463,8 +463,9 @@ void LazyListView::relayout() {
|
||||||
bool hasVisItem = false;
|
bool hasVisItem = false;
|
||||||
for (int i = 0; i < static_cast<int>(m_layout.size()); ++i) {
|
for (int i = 0; i < static_cast<int>(m_layout.size()); ++i) {
|
||||||
qreal h;
|
qreal h;
|
||||||
if (m_delegates.contains(i) && m_delegates[i].item)
|
auto dit = m_delegates.find(i);
|
||||||
h = delegateVisibleHeight(m_delegates[i].item);
|
if (dit != m_delegates.end() && dit->item)
|
||||||
|
h = delegateVisibleHeight(dit->item);
|
||||||
else
|
else
|
||||||
h = m_layout[i].heightKnown ? m_layout[i].height : effectiveEstimatedHeight();
|
h = m_layout[i].heightKnown ? m_layout[i].height : effectiveEstimatedHeight();
|
||||||
if (h > 0) {
|
if (h > 0) {
|
||||||
|
|
@ -573,7 +574,10 @@ void LazyListView::syncDelegates() {
|
||||||
for (int idx : toRemove) {
|
for (int idx : toRemove) {
|
||||||
if (destroyed >= destroyBudget)
|
if (destroyed >= destroyBudget)
|
||||||
break;
|
break;
|
||||||
removedEntries.append(m_delegates.take(idx));
|
auto entry = m_delegates.take(idx);
|
||||||
|
if (entry.item)
|
||||||
|
m_itemToIndex.remove(entry.item);
|
||||||
|
removedEntries.append(std::move(entry));
|
||||||
++destroyed;
|
++destroyed;
|
||||||
}
|
}
|
||||||
for (auto& entry : removedEntries)
|
for (auto& entry : removedEntries)
|
||||||
|
|
@ -608,6 +612,7 @@ void LazyListView::syncDelegates() {
|
||||||
layoutChanged = true;
|
layoutChanged = true;
|
||||||
}
|
}
|
||||||
entry.item->setY(m_layout[i].targetY - m_contentY);
|
entry.item->setY(m_layout[i].targetY - m_contentY);
|
||||||
|
m_itemToIndex.insert(entry.item, i);
|
||||||
m_delegates.insert(i, std::move(entry));
|
m_delegates.insert(i, std::move(entry));
|
||||||
++created;
|
++created;
|
||||||
}
|
}
|
||||||
|
|
@ -692,35 +697,32 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
||||||
if (addingAttached)
|
if (addingAttached)
|
||||||
addingAttached->setAdding(false);
|
addingAttached->setAdding(false);
|
||||||
|
|
||||||
// Shared height-change handler — captures item pointer instead of index
|
// Height-change handler — uses m_itemToIndex for O(1) lookup
|
||||||
// so it remains valid after model inserts/removes/moves shift indices.
|
|
||||||
auto onHeightChanged = [this, item = entry.item] {
|
auto onHeightChanged = [this, item = entry.item] {
|
||||||
// Find the delegate entry by item pointer
|
auto indexIt = m_itemToIndex.find(item);
|
||||||
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
|
if (indexIt == m_itemToIndex.end())
|
||||||
if (it->item != item)
|
|
||||||
continue;
|
|
||||||
const int idx = it.key();
|
|
||||||
const qreal h = delegateHeight(item);
|
|
||||||
if (idx < static_cast<int>(m_layout.size()) && !qFuzzyCompare(m_layout[idx].height, h)) {
|
|
||||||
const qreal oldH = m_layout[idx].height;
|
|
||||||
const bool wasKnown = m_layout[idx].heightKnown;
|
|
||||||
m_layout[idx].height = h;
|
|
||||||
m_layout[idx].heightKnown = true;
|
|
||||||
if (wasKnown)
|
|
||||||
untrackHeight(oldH);
|
|
||||||
trackHeight(h);
|
|
||||||
// Batch relayout: multiple height changes in the same event loop
|
|
||||||
// iteration are coalesced into a single relayout + polish.
|
|
||||||
if (!m_relayoutPending) {
|
|
||||||
m_relayoutPending = true;
|
|
||||||
QTimer::singleShot(0, this, [this] {
|
|
||||||
m_relayoutPending = false;
|
|
||||||
relayout();
|
|
||||||
polish();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
|
const int idx = indexIt.value();
|
||||||
|
auto delegateIt = m_delegates.find(idx);
|
||||||
|
if (delegateIt == m_delegates.end() || delegateIt->item != item)
|
||||||
|
return;
|
||||||
|
const qreal h = delegateHeight(item);
|
||||||
|
if (idx < static_cast<int>(m_layout.size()) && !qFuzzyCompare(m_layout[idx].height, h)) {
|
||||||
|
const qreal oldH = m_layout[idx].height;
|
||||||
|
const bool wasKnown = m_layout[idx].heightKnown;
|
||||||
|
m_layout[idx].height = h;
|
||||||
|
m_layout[idx].heightKnown = true;
|
||||||
|
if (wasKnown)
|
||||||
|
untrackHeight(oldH);
|
||||||
|
trackHeight(h);
|
||||||
|
if (!m_relayoutPending) {
|
||||||
|
m_relayoutPending = true;
|
||||||
|
QTimer::singleShot(0, this, [this] {
|
||||||
|
m_relayoutPending = false;
|
||||||
|
relayout();
|
||||||
|
polish();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -837,6 +839,7 @@ void LazyListView::resetContent() {
|
||||||
for (auto& entry : m_delegates)
|
for (auto& entry : m_delegates)
|
||||||
destroyDelegate(entry);
|
destroyDelegate(entry);
|
||||||
m_delegates.clear();
|
m_delegates.clear();
|
||||||
|
m_itemToIndex.clear();
|
||||||
|
|
||||||
for (auto& entry : m_dyingDelegates)
|
for (auto& entry : m_dyingDelegates)
|
||||||
destroyDelegate(entry);
|
destroyDelegate(entry);
|
||||||
|
|
@ -883,6 +886,8 @@ void LazyListView::onRowsInserted(const QModelIndex& parent, int first, int last
|
||||||
entry.modelIndex = newIdx;
|
entry.modelIndex = newIdx;
|
||||||
if (entry.context)
|
if (entry.context)
|
||||||
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
|
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
|
||||||
|
if (entry.item)
|
||||||
|
m_itemToIndex[entry.item] = newIdx;
|
||||||
shifted.insert(newIdx, std::move(entry));
|
shifted.insert(newIdx, std::move(entry));
|
||||||
}
|
}
|
||||||
m_delegates = std::move(shifted);
|
m_delegates = std::move(shifted);
|
||||||
|
|
@ -904,11 +909,12 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent, int first,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto entry = m_delegates.take(i);
|
auto entry = m_delegates.take(i);
|
||||||
|
if (entry.item)
|
||||||
|
m_itemToIndex.remove(entry.item);
|
||||||
entry.pendingRemoval = true;
|
entry.pendingRemoval = true;
|
||||||
stopAnimation(entry);
|
stopAnimation(entry);
|
||||||
|
|
||||||
if (m_removeDuration > 0 && entry.item) {
|
if (m_removeDuration > 0 && entry.item) {
|
||||||
// Signal the delegate via attached property — QML handles the visual transition
|
|
||||||
auto* attached =
|
auto* attached =
|
||||||
qobject_cast<LazyListViewAttached*>(qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
|
qobject_cast<LazyListViewAttached*>(qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
|
||||||
if (attached)
|
if (attached)
|
||||||
|
|
@ -955,6 +961,8 @@ void LazyListView::onRowsRemoved(const QModelIndex& parent, int first, int last)
|
||||||
entry.modelIndex = newIdx;
|
entry.modelIndex = newIdx;
|
||||||
if (entry.context)
|
if (entry.context)
|
||||||
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
|
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
|
||||||
|
if (entry.item)
|
||||||
|
m_itemToIndex[entry.item] = newIdx;
|
||||||
shifted.insert(newIdx, std::move(entry));
|
shifted.insert(newIdx, std::move(entry));
|
||||||
}
|
}
|
||||||
m_delegates = std::move(shifted);
|
m_delegates = std::move(shifted);
|
||||||
|
|
@ -998,6 +1006,8 @@ void LazyListView::onRowsMoved(const QModelIndex& parent, int start, int end, co
|
||||||
entry.modelIndex = newIdx;
|
entry.modelIndex = newIdx;
|
||||||
if (entry.context)
|
if (entry.context)
|
||||||
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
|
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
|
||||||
|
if (entry.item)
|
||||||
|
m_itemToIndex[entry.item] = newIdx;
|
||||||
remapped.insert(newIdx, std::move(entry));
|
remapped.insert(newIdx, std::move(entry));
|
||||||
}
|
}
|
||||||
m_delegates = std::move(remapped);
|
m_delegates = std::move(remapped);
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,7 @@ private:
|
||||||
|
|
||||||
QVector<ItemRecord> m_layout;
|
QVector<ItemRecord> m_layout;
|
||||||
QHash<int, DelegateEntry> m_delegates;
|
QHash<int, DelegateEntry> m_delegates;
|
||||||
|
QHash<QQuickItem*, int> m_itemToIndex;
|
||||||
QVector<DelegateEntry> m_dyingDelegates;
|
QVector<DelegateEntry> m_dyingDelegates;
|
||||||
|
|
||||||
int m_activeAnimations = 0;
|
int m_activeAnimations = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue