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
|
implicitHeight: contentHeight
|
||||||
|
|
||||||
spacing: Math.round(Appearance.spacing.small / 2)
|
spacing: Math.round(Appearance.spacing.small / 2)
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
removeDuration: Appearance.anim.durations.normal
|
removeDuration: Appearance.anim.durations.normal
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@
|
||||||
#include <qpropertyanimation.h>
|
#include <qpropertyanimation.h>
|
||||||
#include <qtimer.h>
|
#include <qtimer.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int ASYNC_BATCH_CREATE = 2;
|
||||||
|
constexpr int ASYNC_BATCH_DESTROY = 4;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace caelestia::components {
|
namespace caelestia::components {
|
||||||
|
|
||||||
// --- LazyListViewAttached ---
|
// --- LazyListViewAttached ---
|
||||||
|
|
@ -195,6 +202,17 @@ void LazyListView::setEstimatedHeight(qreal height) {
|
||||||
polish();
|
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 {
|
qreal LazyListView::effectiveEstimatedHeight() const {
|
||||||
if (m_estimatedHeight >= 0)
|
if (m_estimatedHeight >= 0)
|
||||||
return m_estimatedHeight;
|
return m_estimatedHeight;
|
||||||
|
|
@ -531,41 +549,68 @@ void LazyListView::syncDelegates() {
|
||||||
visibleIndices.insert(i);
|
visibleIndices.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy delegates outside visible range (if not animating)
|
// Collect delegates to destroy (outside visible range and not animating)
|
||||||
QList<int> toRemove;
|
QList<int> toRemove;
|
||||||
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
|
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());
|
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) {
|
if (first >= 0) {
|
||||||
for (int i = first; i <= last; ++i) {
|
for (int i = first; i <= last; ++i) {
|
||||||
if (m_delegates.contains(i))
|
if (!m_delegates.contains(i))
|
||||||
continue;
|
toCreate.append(i);
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ class LazyListView : public QQuickItem {
|
||||||
// Sizing
|
// Sizing
|
||||||
Q_PROPERTY(qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight NOTIFY estimatedHeightChanged)
|
Q_PROPERTY(qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight NOTIFY estimatedHeightChanged)
|
||||||
|
|
||||||
|
// Async
|
||||||
|
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
|
||||||
|
|
||||||
// Add Animation
|
// Add Animation
|
||||||
Q_PROPERTY(int addDuration READ addDuration WRITE setAddDuration NOTIFY addDurationChanged)
|
Q_PROPERTY(int addDuration READ addDuration WRITE setAddDuration NOTIFY addDurationChanged)
|
||||||
Q_PROPERTY(QEasingCurve addCurve READ addCurve WRITE setAddCurve NOTIFY addCurveChanged)
|
Q_PROPERTY(QEasingCurve addCurve READ addCurve WRITE setAddCurve NOTIFY addCurveChanged)
|
||||||
|
|
@ -130,6 +133,10 @@ public:
|
||||||
[[nodiscard]] qreal estimatedHeight() const;
|
[[nodiscard]] qreal estimatedHeight() const;
|
||||||
void setEstimatedHeight(qreal height);
|
void setEstimatedHeight(qreal height);
|
||||||
|
|
||||||
|
// Async
|
||||||
|
[[nodiscard]] bool asynchronous() const;
|
||||||
|
void setAsynchronous(bool async);
|
||||||
|
|
||||||
// Add Animation
|
// Add Animation
|
||||||
[[nodiscard]] int addDuration() const;
|
[[nodiscard]] int addDuration() const;
|
||||||
void setAddDuration(int duration);
|
void setAddDuration(int duration);
|
||||||
|
|
@ -178,6 +185,7 @@ signals:
|
||||||
void useCustomViewportChanged();
|
void useCustomViewportChanged();
|
||||||
void cacheBufferChanged();
|
void cacheBufferChanged();
|
||||||
void estimatedHeightChanged();
|
void estimatedHeightChanged();
|
||||||
|
void asynchronousChanged();
|
||||||
void addDurationChanged();
|
void addDurationChanged();
|
||||||
void addCurveChanged();
|
void addCurveChanged();
|
||||||
void addFromOpacityChanged();
|
void addFromOpacityChanged();
|
||||||
|
|
@ -261,6 +269,7 @@ private:
|
||||||
qreal m_estimatedHeight = -1;
|
qreal m_estimatedHeight = -1;
|
||||||
qreal m_knownHeightSum = 0;
|
qreal m_knownHeightSum = 0;
|
||||||
int m_knownHeightCount = 0;
|
int m_knownHeightCount = 0;
|
||||||
|
bool m_asynchronous = false;
|
||||||
|
|
||||||
int m_addDuration = 300;
|
int m_addDuration = 300;
|
||||||
QEasingCurve m_addCurve;
|
QEasingCurve m_addCurve;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue