File indexing completed on 2024-04-21 04:41:45
0001 /*************************************************************************** 0002 * Copyright (C) 2018 by Emmanuel Lepage Vallee * 0003 * Author : Emmanuel Lepage Vallee <emmanuel.lepage@kde.org> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 3 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0017 **************************************************************************/ 0018 #include "contextadapterfactory.h" 0019 0020 // Qt 0021 #include <QtCore/QAbstractItemModel> 0022 #include <QtCore/QMutex> 0023 #include <QtCore/private/qmetaobjectbuilder_p.h> 0024 #include <QQmlContext> 0025 #include <QQuickItem> 0026 #include <QQmlEngine> 0027 0028 // KQuickItemViews 0029 #include "adapters/abstractitemadapter.h" 0030 #include "viewbase.h" 0031 #include "viewport.h" 0032 #include "private/viewport_p.h" 0033 #include "private/indexmetadata_p.h" 0034 #include "extensions/contextextension.h" 0035 #include "adapters/modeladapter.h" 0036 #include "private/statetracker/viewitem_p.h" 0037 #include "adapters/contextadapter.h" 0038 0039 using FactoryFunctor = std::function<ContextAdapter*(QQmlContext*)>; 0040 0041 /** 0042 * Add some metadata to be able to use QAbstractItemModel roles as QObject 0043 * properties. 0044 */ 0045 struct MetaProperty 0046 { 0047 // Bitmask to hold (future) metadata regarding how the property is used 0048 // by QML. 0049 enum Flags : int { 0050 UNUSED = 0x0 << 0, /*!< This property was never used */ 0051 READ = 0x1 << 0, /*!< If data() was ever called */ 0052 HAS_DATA = 0x1 << 1, /*!< When the QVariant is valid */ 0053 TRIED_WRITE = 0x1 << 2, /*!< When setData returns false */ 0054 HAS_WRITTEN = 0x1 << 3, /*!< When setData returns true */ 0055 HAS_CHANGED = 0x1 << 4, /*!< When the value was queried many times */ 0056 HAS_SUBSET = 0x1 << 5, /*!< When dataChanged has a role list */ 0057 HAS_GLOBAL = 0x1 << 6, /*!< When dataChanged has no role */ 0058 IS_ROLE = 0x1 << 7, /*!< When the MetaProperty map to a model role */ 0059 }; 0060 int flags {Flags::UNUSED}; 0061 0062 /// Q_PROPERTY internal id 0063 int propId; 0064 0065 /// The role ID from QAbstractItemModel::roleNames 0066 int roleId {-1}; 0067 0068 /** 0069 * The name ID from QAbstractItemModel::roleNames 0070 * 0071 * (the pointer *is* on purpose reduce cache faults in this hot code path) 0072 */ 0073 QByteArray* name {nullptr}; 0074 0075 uint signalId; 0076 }; 0077 0078 struct GroupMetaData 0079 { 0080 ContextExtension* ptr; 0081 uint offset; 0082 }; 0083 0084 /** 0085 * Keep the offset and id of the extension for validation and change notification. 0086 */ 0087 class ContextExtensionPrivate 0088 { 0089 public: 0090 uint m_Id {0}; 0091 uint m_Offset {0}; 0092 ContextAdapterFactoryPrivate *d_ptr {nullptr}; 0093 }; 0094 0095 /** 0096 * This struct is the internal representation normally built by the Qt MOC 0097 * generator. 0098 * 0099 * It holds a "fake" type of QObject designed to reflect the model roles as 0100 * QObject properties. It also tracks the property being *used* by QML to 0101 * prevent too many events being pushed into the QML context. 0102 */ 0103 struct DynamicMetaType final 0104 { 0105 explicit DynamicMetaType(const QHash<int, QByteArray>& roles); 0106 ~DynamicMetaType() { 0107 delete[] roles;//FIXME leak [roleCount*sizeof(MetaProperty))]; 0108 } 0109 0110 const size_t roleCount { 0 }; 0111 size_t propertyCount { 0 }; 0112 MetaProperty* roles {nullptr}; 0113 QSet<MetaProperty*> m_lUsed { }; 0114 QMetaObject *m_pMetaObject {nullptr}; 0115 bool m_GroupInit { false }; 0116 QHash<int, MetaProperty*> m_hRoleIds { }; 0117 uint8_t *m_pCacheMap {nullptr}; 0118 0119 /** 0120 * Assuming the number of role is never *that* high, keep a jump map to 0121 * prevent doing dispatch vTable when checking the properties source. 0122 * 0123 * In theory it can be changed at runtime if the need arise, but for now 0124 * its static. Better harden the binaries a bit, having call maps on the 0125 * heap isn't the most secure scheme in the universe. 0126 */ 0127 GroupMetaData* m_lGroupMapping {nullptr}; 0128 }; 0129 0130 class DynamicContext final : public QObject 0131 { 0132 public: 0133 explicit DynamicContext(ContextAdapterFactory* mt); 0134 virtual ~DynamicContext(); 0135 0136 // Some "secret" QObject methods. 0137 virtual int qt_metacall(QMetaObject::Call call, int id, void **argv) override; 0138 virtual void* qt_metacast(const char *name) override; 0139 virtual const QMetaObject *metaObject() const override; 0140 0141 // Use a C array to prevent the array bound checks 0142 QVariant **m_lVariants {nullptr}; 0143 DynamicMetaType * m_pMetaType {nullptr}; 0144 bool m_Cache { true }; 0145 QQmlContext * m_pCtx {nullptr}; 0146 QPersistentModelIndex m_Index { }; 0147 QQmlContext * m_pParentCtx{nullptr}; 0148 QMetaObject::Connection m_Conn; 0149 0150 ContextAdapterFactoryPrivate* d_ptr {nullptr}; 0151 ContextAdapter* m_pBuilder; 0152 }; 0153 0154 class ContextAdapterFactoryPrivate 0155 { 0156 public: 0157 // Attributes 0158 QList<ContextExtension*> m_lGroups { }; 0159 mutable DynamicMetaType *m_pMetaType {nullptr}; 0160 QAbstractItemModel *m_pModel {nullptr}; 0161 0162 FactoryFunctor m_fFactory; 0163 0164 // Helper 0165 void initGroup(const QHash<int, QByteArray>& rls); 0166 void finish(); 0167 0168 ContextAdapterFactory* q_ptr; 0169 }; 0170 0171 /** 0172 * Create a group of virtual Q_PROPERTY to match the model role names. 0173 */ 0174 class RoleGroup final : public ContextExtension 0175 { 0176 public: 0177 explicit RoleGroup(ContextAdapterFactoryPrivate* d) : d_ptr(d) {} 0178 0179 // The implementation isn't necessary in this case given it uses a second 0180 // layer of vTable instead of a static list. It goes against the 0181 // documentation, but that's on purpose. 0182 virtual QVariant getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const override; 0183 virtual uint size() const override; 0184 virtual QByteArray getPropertyName(uint id) const override; 0185 virtual bool setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const; 0186 0187 0188 ContextAdapterFactoryPrivate* d_ptr; 0189 }; 0190 0191 ContextAdapterFactory::ContextAdapterFactory(FactoryFunctor f) : d_ptr(new ContextAdapterFactoryPrivate()) 0192 { 0193 d_ptr->q_ptr = this; 0194 d_ptr->m_fFactory = f; 0195 0196 addContextExtension(new RoleGroup(d_ptr)); 0197 } 0198 0199 ContextAdapterFactory::~ContextAdapterFactory() 0200 { 0201 delete d_ptr; 0202 } 0203 0204 uint ContextExtension::size() const 0205 { 0206 return propertyNames().size(); 0207 } 0208 0209 bool ContextExtension::supportCaching(uint id) const 0210 { 0211 Q_UNUSED(id) 0212 return true; 0213 } 0214 0215 QVector<QByteArray>& ContextExtension::propertyNames() const 0216 { 0217 static QVector<QByteArray> r; 0218 return r; 0219 } 0220 0221 QByteArray ContextExtension::getPropertyName(uint id) const 0222 { 0223 return propertyNames()[id]; 0224 } 0225 0226 bool ContextExtension::setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const 0227 { 0228 Q_UNUSED(item) 0229 Q_UNUSED(id) 0230 Q_UNUSED(value) 0231 Q_UNUSED(index) 0232 return false; 0233 } 0234 0235 void ContextExtension::changeProperty(AbstractItemAdapter* item, uint id) 0236 { 0237 Q_UNUSED(item) 0238 Q_UNUSED(id) 0239 Q_ASSERT(false); 0240 /* 0241 const auto metaRole = &d_ptr->d_ptr->m_pMetaType->roles[id]; 0242 Q_ASSERT(metaRole); 0243 0244 auto mo = d_ptr->d_ptr->m_pMetaType->m_pMetaObject; 0245 0246 index()*/ 0247 } 0248 0249 void ContextAdapter::flushCache() 0250 { 0251 for (uint i = 0; i < d_ptr->m_pMetaType->propertyCount; i++) { 0252 if (d_ptr->m_lVariants[i]) 0253 delete d_ptr->m_lVariants[i]; 0254 0255 d_ptr->m_lVariants[i] = nullptr; 0256 } 0257 } 0258 0259 void AbstractItemAdapter::dismissCacheEntry(ContextExtension* e, int id) 0260 { 0261 auto dx = s_ptr->m_pMetadata->contextAdapter()->d_ptr; 0262 if (!dx) 0263 return; 0264 0265 Q_ASSERT(e); 0266 Q_ASSERT(e->d_ptr->d_ptr->q_ptr == viewport()->modelAdapter()->contextAdapterFactory()); 0267 Q_ASSERT(e->d_ptr->d_ptr->m_lGroups[e->d_ptr->m_Id] == e); 0268 Q_ASSERT(id >= 0 && id < (int) e->size()); 0269 0270 dx->m_lVariants[e->d_ptr->m_Offset + id] = nullptr; 0271 } 0272 0273 QVariant RoleGroup::getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const 0274 { 0275 Q_UNUSED(item) 0276 const auto metaRole = &d_ptr->m_pMetaType->roles[id]; 0277 0278 // Keep track of the accessed roles 0279 if (!(metaRole->flags & MetaProperty::Flags::READ)) 0280 d_ptr->m_pMetaType->m_lUsed << metaRole; 0281 0282 metaRole->flags |= MetaProperty::Flags::READ; 0283 0284 return index.data(metaRole->roleId); 0285 } 0286 0287 uint RoleGroup::size() const 0288 { 0289 return d_ptr->m_pMetaType->roleCount; 0290 } 0291 0292 QByteArray RoleGroup::getPropertyName(uint id) const 0293 { 0294 return *d_ptr->m_pMetaType->roles[id].name; 0295 } 0296 0297 bool RoleGroup::setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const 0298 { 0299 // Avoid "useless" setData. With binding loops this can get very nasty. 0300 // Yes: You could to do this on purpose in the past, but it's no longer 0301 // safe. 0302 if (getProperty(item, id, index) == value) 0303 return false; 0304 0305 const auto metaRole = &d_ptr->m_pMetaType->roles[id]; 0306 0307 // Keep track of the accessed roles 0308 if (!(metaRole->flags & MetaProperty::Flags::TRIED_WRITE)) 0309 d_ptr->m_pMetaType->m_lUsed << metaRole; 0310 0311 metaRole->flags |= MetaProperty::Flags::TRIED_WRITE; 0312 0313 if (d_ptr->m_pModel->setData(index, value, metaRole->roleId)) { 0314 metaRole->flags |= MetaProperty::Flags::HAS_WRITTEN; 0315 return true; 0316 } 0317 0318 return false; 0319 } 0320 0321 const QMetaObject *DynamicContext::metaObject() const 0322 { 0323 Q_ASSERT(m_pMetaType); 0324 return m_pMetaType->m_pMetaObject; 0325 } 0326 0327 int DynamicContext::qt_metacall(QMetaObject::Call call, int id, void **argv) 0328 { 0329 const int realId = id - m_pMetaType->m_pMetaObject->propertyOffset(); 0330 0331 //qDebug() << "META" << id << realId << call << QMetaObject::ReadProperty; 0332 if (realId < 0) 0333 return QObject::qt_metacall(call, id, argv); 0334 0335 const auto group = &m_pMetaType->m_lGroupMapping[realId]; 0336 Q_ASSERT(group->ptr); 0337 0338 if (call == QMetaObject::ReadProperty) { 0339 if (Q_UNLIKELY(((size_t)realId) >= m_pMetaType->propertyCount)) { 0340 Q_ASSERT(false); 0341 return -1; 0342 } 0343 0344 const QModelIndex idx = m_pBuilder->item() ? m_pBuilder->item()->index() : m_Index; 0345 0346 const bool supportsCache = m_Cache && 0347 (m_pMetaType->m_pCacheMap[realId/8] & (1 << (realId % 8))); 0348 0349 // Use a special function for the role case. It's only known at runtime. 0350 QVariant *value = m_lVariants[realId] && supportsCache 0351 ? m_lVariants[realId] : new QVariant( 0352 group->ptr->getProperty(m_pBuilder->item(), realId - group->offset, idx)); 0353 0354 if (supportsCache && !m_lVariants[realId]) 0355 m_lVariants[realId] = value; 0356 0357 QMetaType::construct(QMetaType::QVariant, argv[0], value->data()); 0358 0359 // if (!supportsCache) 0360 // delete value; 0361 } 0362 else if (call == QMetaObject::WriteProperty) { 0363 const QVariant value = QVariant(QMetaType::QVariant, argv[0]).value<QVariant>(); 0364 const QModelIndex idx = m_pBuilder->item() ? m_pBuilder->item()->index() : m_Index; 0365 0366 const bool ret = group->ptr->setProperty( 0367 m_pBuilder->item(), realId - group->offset, value, idx 0368 ); 0369 0370 // Register if setting the property worked 0371 *reinterpret_cast<int*>(argv[2]) = ret ? 1 : 0; 0372 0373 QMetaObject::activate(this, m_pMetaType->m_pMetaObject, realId, argv); 0374 0375 } 0376 else if (call == QMetaObject::InvokeMetaMethod) { 0377 int sigId = id - m_pMetaType->m_pMetaObject->methodOffset(); 0378 qDebug() << "LA LA INVOKE" << sigId << id; 0379 QMetaObject::activate(this, m_pMetaType->m_pMetaObject, id, nullptr); 0380 return -1; 0381 } 0382 0383 return -1; 0384 } 0385 0386 void* DynamicContext::qt_metacast(const char *name) 0387 { 0388 if (!strcmp(name, m_pMetaType->m_pMetaObject->className())) 0389 return this; 0390 0391 return QObject::qt_metacast(name); 0392 } 0393 0394 DynamicMetaType::DynamicMetaType(const QHash<int, QByteArray>& rls) : 0395 roleCount(rls.size()) 0396 {} 0397 0398 /// Populate a vTable with the propertyId -> group object 0399 void ContextAdapterFactoryPrivate::initGroup(const QHash<int, QByteArray>& rls) 0400 { 0401 Q_ASSERT(!m_pMetaType->m_GroupInit); 0402 0403 for (auto group : qAsConst(m_lGroups)) 0404 m_pMetaType->propertyCount += group->size(); 0405 0406 m_pMetaType->m_lGroupMapping = (GroupMetaData*) malloc( 0407 sizeof(GroupMetaData) * m_pMetaType->propertyCount 0408 ); 0409 0410 uint offset(0), realId(0), groupId(0); 0411 0412 for (auto group : qAsConst(m_lGroups)) { 0413 Q_ASSERT(!group->d_ptr->d_ptr); 0414 group->d_ptr->d_ptr = this; 0415 group->d_ptr->m_Offset = offset; 0416 group->d_ptr->m_Id = groupId++; 0417 0418 const uint gs = group->size(); 0419 0420 for (uint i = 0; i < gs; i++) 0421 m_pMetaType->m_lGroupMapping[offset+i] = {group, offset}; 0422 0423 offset += gs; 0424 } 0425 Q_ASSERT(offset == m_pMetaType->propertyCount); 0426 0427 // Add a bitfield to store the properties that need to skip the cache 0428 const int fieldSize = m_pMetaType->propertyCount / 8 + (m_pMetaType->propertyCount%8?1:0); 0429 m_pMetaType->m_pCacheMap = (uint8_t*) malloc(fieldSize); 0430 for (int i = 0; i < fieldSize; i++) 0431 m_pMetaType->m_pCacheMap[i] = 0; 0432 0433 m_pMetaType->m_GroupInit = true; 0434 0435 // Create the metaobject 0436 QMetaObjectBuilder builder; 0437 builder.setClassName("DynamicContext"); 0438 builder.setSuperClass(&QObject::staticMetaObject); 0439 0440 // Use a C array like the moc would do because this is called **A LOT** 0441 m_pMetaType->roles = new MetaProperty[m_pMetaType->propertyCount]; 0442 0443 // Setup the role metadata 0444 for (auto i = rls.constBegin(); i != rls.constEnd(); i++) { 0445 uint id = realId++; 0446 MetaProperty* r = &m_pMetaType->roles[id]; 0447 0448 r->roleId = i.key(); 0449 r->name = new QByteArray(i.value()); 0450 r->flags |= MetaProperty::Flags::IS_ROLE; 0451 0452 m_pMetaType->m_hRoleIds[i.key()] = r; 0453 } 0454 0455 realId = 0; 0456 0457 // Add all object virtual properties 0458 for (const auto g : qAsConst(m_lGroups)) { 0459 for (uint j = 0; j < g->size(); j++) { 0460 uint id = realId++; 0461 Q_ASSERT(id < m_pMetaType->propertyCount); 0462 0463 MetaProperty* r = &m_pMetaType->roles[id]; 0464 r->propId = id; 0465 const auto name = g->getPropertyName(j); 0466 0467 auto property = builder.addProperty(name, "QVariant"); 0468 property.setWritable(true); 0469 0470 auto signal = builder.addSignal(name + "Changed()"); 0471 r->signalId = signal.index(); 0472 property.setNotifySignal(signal); 0473 0474 // Set the cache bit 0475 m_pMetaType->m_pCacheMap[id/8] |= (g->supportCaching(j)?1:0) << (id % 8); 0476 } 0477 } 0478 0479 m_pMetaType->m_pMetaObject = builder.toMetaObject(); 0480 } 0481 0482 DynamicContext::DynamicContext(ContextAdapterFactory* cm) : 0483 m_pMetaType(cm->d_ptr->m_pMetaType) 0484 { 0485 Q_ASSERT(m_pMetaType); 0486 Q_ASSERT(m_pMetaType->roleCount <= m_pMetaType->propertyCount); 0487 0488 m_lVariants = (QVariant**) malloc(sizeof(QVariant*)*m_pMetaType->propertyCount); 0489 0490 //TODO SIMD this 0491 for (uint i = 0; i < m_pMetaType->propertyCount; i++) 0492 m_lVariants[i] = nullptr; 0493 } 0494 0495 DynamicContext::~DynamicContext() 0496 {} 0497 0498 //FIXME delete the metatype now that it's invalid. 0499 // if (m_pMetaType) { 0500 // qDeleteAll(m_hContextMapper); 0501 // m_hContextMapper.clear(); 0502 // 0503 // delete m_pMetaType; 0504 // m_pMetaType = nullptr; 0505 // } 0506 0507 bool ContextAdapter::updateRoles(const QVector<int> &modified) const 0508 { 0509 if (!d_ptr->d_ptr->m_pMetaType) 0510 return false; 0511 0512 bool ret = false; 0513 0514 if (!modified.isEmpty()) { 0515 for (auto r : qAsConst(modified)) { 0516 if (auto mr = d_ptr->d_ptr->m_pMetaType->m_hRoleIds.value(r)) { 0517 // This works because the role offset is always 0 0518 if (d_ptr->m_lVariants[mr->propId]) { 0519 delete d_ptr->m_lVariants[mr->propId]; 0520 d_ptr->m_lVariants[mr->propId] = nullptr; 0521 QMetaMethod m = d_ptr->metaObject()->method(mr->signalId); 0522 m.invoke(d_ptr); 0523 ret |= ret; 0524 } 0525 } 0526 } 0527 } 0528 else { 0529 // Only update the roles known to have an impact 0530 for (auto mr : qAsConst(d_ptr->d_ptr->m_pMetaType->m_lUsed)) { 0531 // Use `READ` instead of checking the cache because it could have 0532 // been dismissed for many reasons. 0533 if ((!d_ptr->m_Cache) || mr->flags & MetaProperty::Flags::READ) { 0534 if (auto v = d_ptr->m_lVariants[mr->propId]) 0535 delete v; 0536 0537 d_ptr->m_lVariants[mr->propId] = nullptr; 0538 0539 //FIXME this should work, but it doesn't 0540 auto mo = d_ptr->d_ptr->m_pMetaType->m_pMetaObject; 0541 const int methodId = mo->methodOffset() + mr->signalId; 0542 QMetaMethod m = mo->method(methodId); 0543 //m.invoke(d_ptr); 0544 Q_ASSERT(m.name() == (*mr->name)+"Changed"); 0545 0546 //FIXME this should also work, but also doesn't 0547 //QMetaObject::activate(d_ptr, mo, mr->signalId, nullptr); 0548 0549 //FIXME Use this for now, but it prevent setData from being implemented 0550 d_ptr->setProperty(*mr->name, d_ptr->property(*mr->name)); 0551 0552 QMetaObject::activate(d_ptr, mo, mr->signalId, nullptr); 0553 0554 ret = true; 0555 } 0556 } 0557 } 0558 0559 return ret; 0560 } 0561 0562 QAbstractItemModel *ContextAdapterFactory::model() const 0563 { 0564 return d_ptr->m_pModel; 0565 } 0566 0567 void ContextAdapterFactory::setModel(QAbstractItemModel *m) 0568 { 0569 d_ptr->m_pModel = m; 0570 } 0571 0572 void ContextAdapterFactoryPrivate::finish() 0573 { 0574 Q_ASSERT(m_pModel); 0575 0576 if (m_pMetaType) 0577 return; 0578 0579 const auto roles = m_pModel->roleNames(); 0580 0581 m_pMetaType = new DynamicMetaType(roles); 0582 initGroup(roles); 0583 } 0584 0585 void ContextAdapterFactory::addContextExtension(ContextExtension* pg) 0586 { 0587 Q_ASSERT(!d_ptr->m_pMetaType); 0588 0589 if (d_ptr->m_pMetaType) { 0590 qWarning() << "It is not possible to add property group after creating a builder"; 0591 return; 0592 } 0593 0594 d_ptr->m_lGroups << pg; 0595 } 0596 0597 QSet<QByteArray> ContextAdapterFactory::usedRoles() const 0598 { 0599 if (!d_ptr->m_pMetaType) 0600 return {}; 0601 0602 QSet<QByteArray> ret; 0603 0604 for (const auto mr : qAsConst(d_ptr->m_pMetaType->m_lUsed)) { 0605 if (mr->roleId != -1) 0606 ret << *mr->name; 0607 } 0608 0609 return ret; 0610 } 0611 0612 ContextAdapter* 0613 ContextAdapterFactory::createAdapter(FactoryFunctor f, QQmlContext *parentContext) const 0614 { 0615 ContextAdapter* ret = f(parentContext); 0616 0617 Q_ASSERT(!ret->d_ptr); 0618 0619 d_ptr->finish(); 0620 ret->d_ptr = new DynamicContext(const_cast<ContextAdapterFactory*>(this)); 0621 ret->d_ptr->d_ptr = d_ptr; 0622 ret->d_ptr->m_pBuilder = ret; 0623 ret->d_ptr->m_pParentCtx = parentContext; 0624 0625 // Use the core application because the parentContext have an unpredictable 0626 // lifecycle and the object may be reparented anyway. If it has no parent, 0627 // QtQuick can take ownership and will also destroy it. 0628 ret->d_ptr->setParent(QCoreApplication::instance()); 0629 0630 //HACK QtQuick ignores 0631 ret->d_ptr->m_Conn = QObject::connect(ret->d_ptr, &QObject::destroyed, ret->d_ptr, [ret, this, parentContext]() { 0632 qWarning() << "Rebuilding the cache because QtQuick bugs trashed it"; 0633 ret->d_ptr = new DynamicContext(const_cast<ContextAdapterFactory*>(this)); 0634 ret->d_ptr->d_ptr = d_ptr; 0635 ret->d_ptr->setParent(QCoreApplication::instance()); 0636 ret->d_ptr->m_pBuilder = ret; 0637 ret->d_ptr->m_pParentCtx = parentContext; 0638 }); 0639 0640 return ret; 0641 } 0642 0643 ContextAdapter* ContextAdapterFactory::createAdapter(QQmlContext *parentContext) const 0644 { 0645 return createAdapter(d_ptr->m_fFactory, parentContext); 0646 } 0647 0648 ContextAdapter::ContextAdapter(QQmlContext *parentContext) 0649 { 0650 Q_UNUSED(parentContext) 0651 } 0652 0653 ContextAdapter::~ContextAdapter() 0654 { 0655 if (d_ptr->m_pCtx) 0656 d_ptr->m_pCtx->setContextObject(nullptr); 0657 0658 QObject::disconnect(d_ptr->m_Conn); 0659 0660 d_ptr->m_pBuilder = nullptr; 0661 0662 delete d_ptr; 0663 } 0664 0665 bool ContextAdapter::isCacheEnabled() const 0666 { 0667 return d_ptr->m_Cache; 0668 } 0669 0670 void ContextAdapter::setCacheEnabled(bool v) 0671 { 0672 d_ptr->m_Cache = v; 0673 } 0674 0675 QModelIndex ContextAdapter::index() const 0676 { 0677 return d_ptr->m_Index; 0678 } 0679 0680 void ContextAdapter::setModelIndex(const QModelIndex& index) 0681 { 0682 const bool hasIndex = d_ptr->m_Index.isValid(); 0683 0684 if (d_ptr->m_Cache) 0685 flushCache(); 0686 0687 d_ptr->m_Index = index; 0688 0689 if (hasIndex) 0690 updateRoles({}); 0691 } 0692 0693 QQmlContext* ContextAdapter::context() const 0694 { 0695 Q_ASSERT(d_ptr); 0696 0697 if (!d_ptr->m_pCtx) { 0698 d_ptr->m_pCtx = new QQmlContext(d_ptr->m_pParentCtx, d_ptr->parent()); 0699 d_ptr->m_pCtx->setContextObject(d_ptr); 0700 d_ptr->m_pCtx->engine()->setObjectOwnership( 0701 d_ptr, QQmlEngine::CppOwnership 0702 ); 0703 d_ptr->m_pCtx->engine()->setObjectOwnership( 0704 d_ptr->m_pCtx, QQmlEngine::CppOwnership 0705 ); 0706 0707 // No amount of setObjectOwnership will prevent Qt 5.12 from doing 0708 // what's it's told. Mitigate this. 0709 QObject::connect(d_ptr->m_pCtx, &QObject::destroyed, d_ptr, [this]() { 0710 d_ptr->m_pCtx = nullptr; 0711 }); 0712 } 0713 0714 return d_ptr->m_pCtx; 0715 } 0716 0717 QObject *ContextAdapter::contextObject() const 0718 { 0719 return d_ptr; 0720 } 0721 0722 bool ContextAdapter::isActive() const 0723 { 0724 return d_ptr->m_pCtx; 0725 } 0726 0727 AbstractItemAdapter* ContextAdapter::item() const 0728 { 0729 return nullptr; 0730 } 0731 0732 ContextExtension::ContextExtension() : d_ptr(new ContextExtensionPrivate()) 0733 {} 0734