fix: remove unnecessary qml context creation

This commit is contained in:
2 * r + 2 * t 2026-04-09 22:20:38 +10:00
parent 9145f83639
commit cc54d9bc5b
2 changed files with 37 additions and 68 deletions

View file

@ -1,6 +1,7 @@
#include "lazylistview.hpp"
#include <algorithm>
#include <qqmlcontext.h>
#include <qtimer.h>
namespace {
@ -572,57 +573,43 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
return entry;
const auto roleNames = m_model->roleNames();
const auto role = roleNames.isEmpty() ? Qt::DisplayRole : roleNames.constBegin().key();
// Use the delegate component's creation context directly for beginCreate
// Use the delegate component's creation context for beginCreate
// so bound components (pragma ComponentBehavior: Bound) are accepted.
// A per-delegate child context is kept for data updates.
auto* compContext = m_delegate->creationContext();
auto* parentContext = compContext ? compContext : qmlContext(this);
if (!parentContext)
if (!compContext)
compContext = qmlContext(this);
if (!compContext)
return entry;
entry.context = new QQmlContext(parentContext, this);
// Build property map for both context properties and initial properties
const auto index = m_model->index(modelIndex, 0);
QVariantMap initialProps;
bool hasModelData = false;
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
const auto name = QString::fromUtf8(it.value());
const auto value = m_model->data(index, it.key());
entry.context->setContextProperty(name, value);
initialProps.insert(name, value);
if (name == QStringLiteral("modelData"))
hasModelData = true;
}
entry.context->setContextProperty(QStringLiteral("index"), modelIndex);
initialProps.insert(QStringLiteral("index"), modelIndex);
// Provide modelData for single-role models or if not already provided by role names
if (!hasModelData) {
const auto value = m_model->data(index, role);
entry.context->setContextProperty(QStringLiteral("modelData"), value);
initialProps.insert(QStringLiteral("modelData"), value);
}
// Use the creation context for beginCreate to satisfy bound component checks
// (pragma ComponentBehavior: Bound). Data is passed via setInitialProperties.
auto* creationCtx = compContext ? compContext : parentContext;
auto* obj = m_delegate->beginCreate(creationCtx);
auto* obj = m_delegate->beginCreate(compContext);
entry.item = qobject_cast<QQuickItem*>(obj);
if (!entry.item) {
if (obj)
m_delegate->completeCreate();
delete obj;
delete entry.context;
entry.context = nullptr;
return entry;
}
// Set initial properties to satisfy required property declarations
// Build initial properties from model data
const auto index = m_model->index(modelIndex, 0);
QVariantMap initialProps;
bool hasModelData = false;
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
const auto name = QString::fromUtf8(it.value());
initialProps.insert(name, m_model->data(index, it.key()));
if (name == QStringLiteral("modelData"))
hasModelData = true;
}
initialProps.insert(QStringLiteral("index"), modelIndex);
if (!hasModelData) {
const auto role = roleNames.isEmpty() ? Qt::DisplayRole : roleNames.constBegin().key();
initialProps.insert(QStringLiteral("modelData"), m_model->data(index, role));
}
m_delegate->setInitialProperties(entry.item, initialProps);
entry.item->setParentItem(this);
@ -706,14 +693,10 @@ void LazyListView::destroyDelegate(DelegateEntry& entry) {
entry.item->deleteLater();
entry.item = nullptr;
}
if (entry.context) {
entry.context->deleteLater();
entry.context = nullptr;
}
}
void LazyListView::updateDelegateData(DelegateEntry& entry) {
if (!m_model)
if (!m_model || !entry.item)
return;
const auto roleNames = m_model->roleNames();
@ -722,27 +705,16 @@ void LazyListView::updateDelegateData(DelegateEntry& entry) {
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
const auto name = QString::fromUtf8(it.value());
const auto value = m_model->data(index, it.key());
if (entry.context)
entry.context->setContextProperty(name, value);
if (entry.item)
entry.item->setProperty(name.toUtf8().constData(), value);
entry.item->setProperty(name.toUtf8().constData(), m_model->data(index, it.key()));
if (name == QStringLiteral("modelData"))
hasModelData = true;
}
if (entry.context)
entry.context->setContextProperty(QStringLiteral("index"), entry.modelIndex);
if (entry.item)
entry.item->setProperty("index", entry.modelIndex);
entry.item->setProperty("index", entry.modelIndex);
if (!hasModelData) {
const auto role = roleNames.isEmpty() ? Qt::DisplayRole : roleNames.constBegin().key();
const auto value = m_model->data(index, role);
if (entry.context)
entry.context->setContextProperty(QStringLiteral("modelData"), value);
if (entry.item)
entry.item->setProperty("modelData", value);
entry.item->setProperty("modelData", m_model->data(index, role));
}
}
@ -824,10 +796,10 @@ void LazyListView::onRowsInserted(const QModelIndex& parent, int first, int last
int newIdx = it.key() >= first ? it.key() + insertCount : it.key();
auto entry = std::move(it.value());
entry.modelIndex = newIdx;
if (entry.context)
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
if (entry.item)
if (entry.item) {
entry.item->setProperty("index", newIdx);
m_itemToIndex[entry.item] = newIdx;
}
shifted.insert(newIdx, std::move(entry));
}
m_delegates = std::move(shifted);
@ -894,10 +866,10 @@ void LazyListView::onRowsRemoved(const QModelIndex& parent, int first, int last)
int newIdx = it.key() > last ? it.key() - removeCount : it.key();
auto entry = std::move(it.value());
entry.modelIndex = newIdx;
if (entry.context)
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
if (entry.item)
if (entry.item) {
entry.item->setProperty("index", newIdx);
m_itemToIndex[entry.item] = newIdx;
}
shifted.insert(newIdx, std::move(entry));
}
m_delegates = std::move(shifted);
@ -939,10 +911,10 @@ void LazyListView::onRowsMoved(const QModelIndex& parent, int start, int end, co
auto entry = std::move(it.value());
entry.modelIndex = newIdx;
if (entry.context)
entry.context->setContextProperty(QStringLiteral("index"), newIdx);
if (entry.item)
if (entry.item) {
entry.item->setProperty("index", newIdx);
m_itemToIndex[entry.item] = newIdx;
}
remapped.insert(newIdx, std::move(entry));
}
m_delegates = std::move(remapped);

View file

@ -4,7 +4,6 @@
#include <qhash.h>
#include <qobject.h>
#include <qqmlcomponent.h>
#include <qqmlcontext.h>
#include <qqmlintegration.h>
#include <qquickitem.h>
#include <qrect.h>
@ -164,7 +163,6 @@ private:
struct DelegateEntry {
int modelIndex = -1;
QQuickItem* item = nullptr;
QQmlContext* context = nullptr;
bool pendingRemoval = false;
QMetaObject::Connection attachedConnection;
};
@ -196,7 +194,6 @@ private:
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QList<int>& roles);
void onModelReset();
// Members
QAbstractItemModel* m_model = nullptr;
QQmlComponent* m_delegate = nullptr;