feat: listview async delegate creation and destruction
This commit is contained in:
parent
ea776c20e0
commit
700ad61969
3 changed files with 81 additions and 26 deletions
|
|
@ -33,6 +33,7 @@ LazyListView {
|
|||
implicitHeight: contentHeight
|
||||
|
||||
spacing: Math.round(Appearance.spacing.small / 2)
|
||||
asynchronous: true
|
||||
|
||||
removeDuration: Appearance.anim.durations.normal
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@
|
|||
#include <qpropertyanimation.h>
|
||||
#include <qtimer.h>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int ASYNC_BATCH_CREATE = 2;
|
||||
constexpr int ASYNC_BATCH_DESTROY = 4;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace caelestia::components {
|
||||
|
||||
// --- LazyListViewAttached ---
|
||||
|
|
@ -195,6 +202,17 @@ void LazyListView::setEstimatedHeight(qreal height) {
|
|||
polish();
|
||||
}
|
||||
|
||||
bool LazyListView::asynchronous() const {
|
||||
return m_asynchronous;
|
||||
}
|
||||
|
||||
void LazyListView::setAsynchronous(bool async) {
|
||||
if (m_asynchronous == async)
|
||||
return;
|
||||
m_asynchronous = async;
|
||||
emit asynchronousChanged();
|
||||
}
|
||||
|
||||
qreal LazyListView::effectiveEstimatedHeight() const {
|
||||
if (m_estimatedHeight >= 0)
|
||||
return m_estimatedHeight;
|
||||
|
|
@ -531,41 +549,68 @@ void LazyListView::syncDelegates() {
|
|||
visibleIndices.insert(i);
|
||||
}
|
||||
|
||||
// Destroy delegates outside visible range (if not animating)
|
||||
// Collect delegates to destroy (outside visible range and not animating)
|
||||
QList<int> toRemove;
|
||||
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
|
||||
if (!visibleIndices.contains(it.key()) && !it->animation) {
|
||||
if (!visibleIndices.contains(it.key()) && !it->animation)
|
||||
toRemove.append(it.key());
|
||||
}
|
||||
}
|
||||
for (int idx : toRemove) {
|
||||
auto entry = m_delegates.take(idx);
|
||||
destroyDelegate(entry);
|
||||
}
|
||||
|
||||
// Create delegates for newly visible indices
|
||||
// Batch destroy
|
||||
const int destroyBudget = m_asynchronous ? ASYNC_BATCH_DESTROY : static_cast<int>(toRemove.size());
|
||||
QVector<DelegateEntry> removedEntries;
|
||||
removedEntries.reserve(std::min(destroyBudget, static_cast<int>(toRemove.size())));
|
||||
int destroyed = 0;
|
||||
for (int idx : toRemove) {
|
||||
if (destroyed >= destroyBudget)
|
||||
break;
|
||||
removedEntries.append(m_delegates.take(idx));
|
||||
++destroyed;
|
||||
}
|
||||
for (auto& entry : removedEntries)
|
||||
destroyDelegate(entry);
|
||||
|
||||
// Collect indices to create
|
||||
QList<int> toCreate;
|
||||
if (first >= 0) {
|
||||
for (int i = first; i <= last; ++i) {
|
||||
if (m_delegates.contains(i))
|
||||
continue;
|
||||
|
||||
auto entry = createDelegate(i);
|
||||
if (entry.item) {
|
||||
// Measure height (prefer attached preferredHeight, fall back to implicitHeight)
|
||||
const qreal h = delegateHeight(entry.item);
|
||||
if (!m_layout[i].heightKnown || !qFuzzyCompare(m_layout[i].height, h)) {
|
||||
if (m_layout[i].heightKnown)
|
||||
untrackHeight(m_layout[i].height);
|
||||
m_layout[i].height = h;
|
||||
m_layout[i].heightKnown = true;
|
||||
trackHeight(h);
|
||||
}
|
||||
// Position immediately so it doesn't flash at y=0
|
||||
entry.item->setY(m_layout[i].targetY - m_contentY);
|
||||
m_delegates.insert(i, std::move(entry));
|
||||
}
|
||||
if (!m_delegates.contains(i))
|
||||
toCreate.append(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch create
|
||||
const int createBudget = m_asynchronous ? ASYNC_BATCH_CREATE : static_cast<int>(toCreate.size());
|
||||
int created = 0;
|
||||
bool layoutChanged = false;
|
||||
for (int i : toCreate) {
|
||||
if (created >= createBudget)
|
||||
break;
|
||||
|
||||
auto entry = createDelegate(i);
|
||||
if (entry.item) {
|
||||
const qreal h = delegateHeight(entry.item);
|
||||
if (!m_layout[i].heightKnown || !qFuzzyCompare(m_layout[i].height, h)) {
|
||||
if (m_layout[i].heightKnown)
|
||||
untrackHeight(m_layout[i].height);
|
||||
m_layout[i].height = h;
|
||||
m_layout[i].heightKnown = true;
|
||||
trackHeight(h);
|
||||
layoutChanged = true;
|
||||
}
|
||||
entry.item->setY(m_layout[i].targetY - m_contentY);
|
||||
m_delegates.insert(i, std::move(entry));
|
||||
++created;
|
||||
}
|
||||
}
|
||||
|
||||
if (layoutChanged)
|
||||
relayout();
|
||||
|
||||
// If async and there's remaining work, schedule another pass
|
||||
if (m_asynchronous &&
|
||||
(destroyed < static_cast<int>(toRemove.size()) || created < static_cast<int>(toCreate.size())))
|
||||
polish();
|
||||
}
|
||||
|
||||
LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
||||
|
|
|
|||
|
|
@ -73,6 +73,9 @@ class LazyListView : public QQuickItem {
|
|||
// Sizing
|
||||
Q_PROPERTY(qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight NOTIFY estimatedHeightChanged)
|
||||
|
||||
// Async
|
||||
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
|
||||
|
||||
// Add Animation
|
||||
Q_PROPERTY(int addDuration READ addDuration WRITE setAddDuration NOTIFY addDurationChanged)
|
||||
Q_PROPERTY(QEasingCurve addCurve READ addCurve WRITE setAddCurve NOTIFY addCurveChanged)
|
||||
|
|
@ -130,6 +133,10 @@ public:
|
|||
[[nodiscard]] qreal estimatedHeight() const;
|
||||
void setEstimatedHeight(qreal height);
|
||||
|
||||
// Async
|
||||
[[nodiscard]] bool asynchronous() const;
|
||||
void setAsynchronous(bool async);
|
||||
|
||||
// Add Animation
|
||||
[[nodiscard]] int addDuration() const;
|
||||
void setAddDuration(int duration);
|
||||
|
|
@ -178,6 +185,7 @@ signals:
|
|||
void useCustomViewportChanged();
|
||||
void cacheBufferChanged();
|
||||
void estimatedHeightChanged();
|
||||
void asynchronousChanged();
|
||||
void addDurationChanged();
|
||||
void addCurveChanged();
|
||||
void addFromOpacityChanged();
|
||||
|
|
@ -261,6 +269,7 @@ private:
|
|||
qreal m_estimatedHeight = -1;
|
||||
qreal m_knownHeightSum = 0;
|
||||
int m_knownHeightCount = 0;
|
||||
bool m_asynchronous = false;
|
||||
|
||||
int m_addDuration = 300;
|
||||
QEasingCurve m_addCurve;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue