diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c5f32..b05df09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,172 +1,174 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0) PROJECT(kquickview) IF(POLICY CMP0063) CMAKE_POLICY(SET CMP0063 NEW) ENDIF(POLICY CMP0063) FIND_PACKAGE(ECM 1.1.0 REQUIRED NO_MODULE) LIST(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") OPTION(BUILD_SHARED_LIBS "" OFF ) INCLUDE(ECMInstallIcons) INCLUDE(ECMOptionalAddSubdirectory) INCLUDE(KDEInstallDirs) INCLUDE(KDECMakeSettings) INCLUDE(KDECompilerSettings) if(NOT BUILD_SHARED_LIBS) set(STATIC_LIBRARY 1) else() set(STATIC_LIBRARY 0) endif() SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTORCC ON) SET(CMAKE_CXX_STANDARD 14) # if(STATIC_LIBRARY) add_definitions(-DQT_PLUGIN) add_definitions(-DQT_STATICPLUGIN=1) # else(STATIC_LIBRARY) # if (BUILD_TESTING) # add_subdirectory(autotests) # endif() # endif(STATIC_LIBRARY) FIND_PACKAGE(Qt5 CONFIG REQUIRED Core Gui Quick QuickControls2 ) add_definitions(-isystem ${Qt5Core_PRIVATE_INCLUDE_DIRS}) if("${CMAKE_BUILD_TYPE}" MATCHES "DEBUG") add_definitions(-DENABLE_EXTRA_VALIDATION=1) endif() SET(GENERIC_LIB_VERSION "1.0.0") #File to compile SET( kquickview_LIB_SRCS # Adapters src/adapters/abstractitemadapter.cpp src/adapters/decorationadapter.cpp src/adapters/modeladapter.cpp src/adapters/scrollbaradapter.cpp src/adapters/selectionadapter.cpp src/adapters/geometryadapter.cpp # Building blocks src/flickablescrollbar.cpp src/plugin.cpp src/proxies/sizehintproxymodel.cpp src/singlemodelviewbase.cpp src/viewbase.cpp src/viewport.cpp src/contextadapterfactory.cpp src/qmodelindexwatcher.cpp + src/qmodelindexbinder.cpp # Views src/views/comboboxview.cpp src/views/flickable.cpp src/views/hierarchyview.cpp src/views/listview.cpp src/views/treeview.cpp src/views/indexview.cpp # State trackers src/private/statetracker/index_p.cpp src/private/statetracker/geometry_p.cpp src/private/statetracker/proximity_p.cpp src/private/statetracker/model_p.cpp src/private/statetracker/modelitem_p.cpp src/private/statetracker/selection_p.cpp src/private/statetracker/content_p.cpp src/private/runtimetests_p.cpp src/private/indexmetadata_p.cpp src/private/geostrategyselector_p.cpp # Geometry strategies src/strategies/justintime.cpp src/strategies/proxy.cpp src/strategies/role.cpp src/strategies/delegate.cpp src/strategies/uniform.cpp src/strategies/aheadoftime.cpp ) set(AUTOMOC_MOC_OPTIONS -Muri=org.kde.playground.kquickview) add_library(kquickview STATIC ${kquickview_LIB_SRCS} ) target_link_libraries( kquickview Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2 ) SET( kquickview_LIB_HDRS adapters/abstractitemadapter.h adapters/contextadapter.h adapters/decorationadapter.h adapters/modeladapter.h adapters/scrollbaradapter.h adapters/selectionadapter.h adapters/geometryadapter.h extensions/contextextension.h flickablescrollbar.h plugin.h proxies/sizehintproxymodel.h singlemodelviewbase.h contextadapterfactory.h qmodelindexwatcher.h + qmodelindexbinder.h viewbase.h views/comboboxview.h views/flickable.h views/indexview.h views/hierarchyview.h views/listview.h views/treeview.h viewport.h # Geometry strategies strategies/justintime.h strategies/proxy.h strategies/role.h strategies/delegate.h strategies/aheadoftime.h strategies/uniform.h ) # Create include file aliases foreach(header ${kquickview_LIB_HDRS}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/${header} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/api/KQuickView/ ) endforeach() target_include_directories(kquickview PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src;${CMAKE_CURRENT_BINARY_DIR}/api" ) set_target_properties(kquickview PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/api;${CMAKE_CURRENT_SOURCE_DIR}/src" ) set_target_properties(kquickview PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/api;${CMAKE_CURRENT_SOURCE_DIR}/src" ) add_subdirectory(tests) include_directories(${CMAKE_CURRENT_BINARY_DIR}/api/) diff --git a/src/adapters/contextadapter.h b/src/adapters/contextadapter.h index 3fd6cb9..0c8c70d 100644 --- a/src/adapters/contextadapter.h +++ b/src/adapters/contextadapter.h @@ -1,91 +1,94 @@ /*************************************************************************** * Copyright (C) 2018 by Emmanuel Lepage Vallee * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ #pragma once // Qt #include class QQmlContext; // KQuickItemViews class AbstractItemAdapter; class ContextAdapterFactory; class DynamicContext; class ContextExtension; /** * Calling `context()` on an instance of this object will create a * QQmlContext reflecting the model roles (and other property groups). * * One instance needs exists per context. It is possible to replace the * QModelIndex later on as needs suit. The two main use cases are to * either track multiple QModelIndex at once and display them or use a * single instance along with some QQmlExpression to add "just-in-time" * properties to objects (like QItemDelegate or * QSortFilterProxyModel::acceptRow) * * These objects MUST BE CREATED AFTER the last call to addContextExtension * has been made and the model(index) has been set. */ class ContextAdapter { friend class AbstractItemAdapter; friend class ContextAdapterFactory; //factory public: virtual ~ContextAdapter(); bool isCacheEnabled() const; void setCacheEnabled(bool v); bool isActive() const; virtual QModelIndex index() const; void setModelIndex(const QModelIndex& index); virtual QQmlContext* context() const final; virtual AbstractItemAdapter* item() const; /** * Clear the cache entry and send the notify signal on the property `id` * from the extension `e`. */ void dismissCache(ContextExtension* e, int id); /** * Notify the adapter some QModelIndex roles changed. * * @return True when the updated roles affect the adapter, false otherwise. */ bool updateRoles(const QVector &modified) const; /** * Clear the cache of role values. * * To improve performance, QAbstractItemModel::data is only called once * unless dataChanged() is called. The value is then stored in the QML * context. Call this to manually invalidate the cache and for roles to be * reloaded. */ void flushCache(); -protected: QObject *contextObject() const; + +protected: explicit ContextAdapter(QQmlContext *parentContext = nullptr); private: DynamicContext* d_ptr {nullptr}; }; + +Q_DECLARE_METATYPE(ContextAdapter*) diff --git a/src/plugin.cpp b/src/plugin.cpp index 0d0bd5e..652746d 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,66 +1,68 @@ /*************************************************************************** * Copyright (C) 2017 by Emmanuel Lepage Vallee * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ #include "plugin.h" #include #include "adapters/decorationadapter.h" #include "adapters/scrollbaradapter.h" #include "adapters/geometryadapter.h" #include "views/hierarchyview.h" #include "views/listview.h" #include "views/treeview.h" #include "qmodelindexwatcher.h" +#include "qmodelindexbinder.h" #include "views/comboboxview.h" #include "views/indexview.h" #include "flickablescrollbar.h" #include "proxies/sizehintproxymodel.h" // Strategies #include "strategies/justintime.h" #include "strategies/role.h" #include "strategies/proxy.h" void KQuickView::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.playground.kquickview")); qmlRegisterType(uri, 1, 0, "HierarchyView"); qmlRegisterType(uri, 1, 0, "TreeView"); qmlRegisterType(uri, 1, 0, "ListView"); qmlRegisterType(uri, 1, 0, "IndexView"); qmlRegisterType(uri, 1, 0, "ScrollBarAdapter"); qmlRegisterType(uri, 1, 0, "GeometryAdapter"); qmlRegisterType(uri, 1,0, "DecorationAdapter"); qmlRegisterType(uri, 1, 0, "ComboBoxView"); qmlRegisterType(uri, 1, 0, "FlickableScrollBar"); qmlRegisterType(uri, 1, 0, "QModelIndexWatcher"); + qmlRegisterType(uri, 1, 0, "QModelIndexBinder"); qmlRegisterType(uri, 1, 0, "SizeHintProxyModel"); qmlRegisterUncreatableType(uri, 1, 0, "ListViewSections", ""); auto suri = QString(QString(uri) + QString(".Strategies")).toLatin1(); qmlRegisterType(suri, 1, 0, "JustInTime"); qmlRegisterType(suri, 1, 0, "Role"); } void KQuickView::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(engine) Q_UNUSED(uri) } diff --git a/src/qmodelindexbinder.cpp b/src/qmodelindexbinder.cpp new file mode 100644 index 0000000..92a4d01 --- /dev/null +++ b/src/qmodelindexbinder.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emmanuel Lepage Vallee * + * Author : Emmanuel Lepage Vallee * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + **************************************************************************/ +#include "qmodelindexbinder.h" + +// Qt +#include +#include +#include + +// KQuickItemViews +#include "qmodelindexwatcher.h" +#include + +class QModelIndexBinderPrivate : public QObject +{ + Q_OBJECT +public: + QByteArray m_Role { }; + QByteArray m_Prop { }; + QObject *m_pContent {nullptr}; + int m_Delay { 0 }; + QTimer *m_pTimer {nullptr}; + bool m_AutoSave { true }; + QQmlContext *m_pCTX {nullptr}; + ContextAdapter *m_pAdapter {nullptr}; + QModelIndexWatcher *m_pWatcher {nullptr}; + bool m_isBinded { false }; + + // Helper + void bind(); + + QModelIndexBinder *q_ptr; + +public Q_SLOTS: + void loadWatcher(); + void slotModelPropChanged(); + void slotObjectPropChanged(); +}; + +QModelIndexBinder::QModelIndexBinder(QQuickItem *parent) : + QQuickItem(parent), d_ptr(new QModelIndexBinderPrivate()) +{ + d_ptr->q_ptr = this; + + //TODO actually track the context, it could be reparented + QTimer::singleShot(0, d_ptr, SLOT(loadWatcher())); +} + +QModelIndexBinder::~QModelIndexBinder() +{ + delete d_ptr; +} + +QString QModelIndexBinder::modelRole() const +{ + return d_ptr->m_Role; +} + +void QModelIndexBinder::setModelRole(const QString& role) +{ + d_ptr->m_Role = role.toLatin1(); + emit changed(); + d_ptr->bind(); +} + +QString QModelIndexBinder::objectProperty() const +{ + return d_ptr->m_Prop; +} + +void QModelIndexBinder::setObjectProperty(const QString& op) +{ + d_ptr->m_Prop = op.toLatin1(); +} + +QObject *QModelIndexBinder::_object() const +{ + return d_ptr->m_pContent; +} + +void QModelIndexBinder::_setObject(QObject *o) +{ + // If the object is a QQuickItem, add it to the view + if (auto w = qobject_cast(o)) + w->setParentItem(this); + + d_ptr->m_pContent = o; + emit changed(); + d_ptr->bind(); +} + +int QModelIndexBinder::delay() const +{ + return d_ptr->m_Delay; +} + +void QModelIndexBinder::setDelay(int d) +{ + d_ptr->m_Delay = d; + emit changed(); +} + +bool QModelIndexBinder::autoSave() const +{ + return d_ptr->m_AutoSave; +} + +void QModelIndexBinder::setAutoSave(bool v) +{ + d_ptr->m_AutoSave = v; + emit changed(); +} + +bool QModelIndexBinder::isSynchronized() const +{ + //TODO + return true; +} + +void QModelIndexBinder::reset() const +{ + //TODO +} + +bool QModelIndexBinder::applyNow() const +{ + //TODO + return true; +} + +void QModelIndexBinderPrivate::loadWatcher() +{ + if (m_pWatcher) + return; + + m_pCTX = QQmlEngine::contextForObject(q_ptr); + Q_ASSERT(m_pCTX); + + if (!m_pCTX) + return; + + auto v = m_pCTX->contextProperty("_modelIndexWatcher"); + auto c = m_pCTX->contextProperty("_contextAdapter"); + + m_pAdapter = qvariant_cast(c); + m_pWatcher = qobject_cast( + qvariant_cast(v) + ); + + bind(); +} + +void QModelIndexBinderPrivate::bind() +{ + if (m_isBinded || m_Role.isEmpty() || m_Prop.isEmpty() || (!m_pContent) || (!m_pWatcher) || (!m_pAdapter)) + return; + + const auto co = m_pAdapter->contextObject(); + + // Find the properties on both side + const int objPropId = m_pContent->metaObject()->indexOfProperty(m_Prop); + const int rolePropId = co->metaObject()->indexOfProperty(m_Role); + Q_ASSERT(objPropId != -1 && rolePropId != -1); + + auto metaProp = m_pContent->metaObject()->property(objPropId); + auto metaRole = co->metaObject()->property(rolePropId); + + // Connect to the metaSlots + auto metaSlotProp = metaObject()->method(metaObject()->indexOfMethod("slotObjectPropChanged()")); + auto metaSlotRole = metaObject()->method(metaObject()->indexOfMethod("slotModelPropChanged()")); + + connect(m_pContent, metaProp.notifySignal(), this, metaSlotProp); + connect(co , metaRole.notifySignal(), this, metaSlotRole); + + // Set the initial value + slotModelPropChanged(); +} + +void QModelIndexBinderPrivate::slotModelPropChanged() +{ + const auto role = m_pAdapter->contextObject()->property(m_Role); + const auto prop = m_pContent->property(m_Prop); + //qDebug() << "ROLE CHANGED" << role << prop; + m_pContent->setProperty(m_Prop, role); +} + +void QModelIndexBinderPrivate::slotObjectPropChanged() +{ + const auto role = m_pAdapter->contextObject()->property(m_Role); + const auto prop = m_pContent->property(m_Prop); + //qDebug() << "PROP CHANGED" << role << prop; + //m_pAdapter->contextObject()->setProperty(m_Role, prop); //FIXME fix the model::setData support +} + +#include diff --git a/src/qmodelindexbinder.h b/src/qmodelindexbinder.h new file mode 100644 index 0000000..66bf974 --- /dev/null +++ b/src/qmodelindexbinder.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emmanuel Lepage Vallee * + * Author : Emmanuel Lepage Vallee * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + **************************************************************************/ +#pragma once + +#include + +class QModelIndexBinderPrivate; + +/** + * + * Bind a children component property with a QModelIndex role. + * + * It is intended to be used within an IndexView. + * + * @todo auto-create as attached properties in IndexView + */ +class Q_DECL_EXPORT QModelIndexBinder : public QQuickItem +{ + Q_OBJECT +public: + + /** + * Only call setData after `delay` milliseconds. + */ + Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY changed) + + /** + * The name of the role to sychronize. + */ + Q_PROPERTY(QString modelRole READ modelRole WRITE setModelRole NOTIFY changed) + + /** + * The (child) object property to bind with the role. + */ + Q_PROPERTY(QString objectProperty READ objectProperty WRITE setObjectProperty NOTIFY changed) + + /** + * Automatically call setData when the data is modified. + */ + Q_PROPERTY(bool autoSave READ autoSave WRITE setAutoSave NOTIFY changed) + + /** + * If the model data match the local one. + */ + Q_PROPERTY(bool synchronized READ isSynchronized NOTIFY changed) + + // The content of the container + Q_PROPERTY(QObject* _object READ _object WRITE _setObject NOTIFY changed) + Q_CLASSINFO("DefaultProperty", "_object") + + explicit QModelIndexBinder(QQuickItem *parent = nullptr); + virtual ~QModelIndexBinder(); + + QString modelRole() const; + void setModelRole(const QString& role); + + QString objectProperty() const; + void setObjectProperty(const QString& op); + + QObject *_object() const; + void _setObject(QObject *o); + + int delay() const; + void setDelay(int d); + + bool autoSave() const; + void setAutoSave(bool v); + + bool isSynchronized() const; + + /** + * Reset the local property to match the model data. + */ + Q_INVOKABLE void reset() const; + + /** + * Call `setData` now. + */ + Q_INVOKABLE bool applyNow() const; + +Q_SIGNALS: + void changed(); + +private: + QModelIndexBinderPrivate *d_ptr; + Q_DECLARE_PRIVATE(QModelIndexBinder) +}; diff --git a/src/qmodelindexwatcher.h b/src/qmodelindexwatcher.h index 2859b55..e290310 100644 --- a/src/qmodelindexwatcher.h +++ b/src/qmodelindexwatcher.h @@ -1,57 +1,57 @@ /*************************************************************************** * Copyright (C) 2018 by Emmanuel Lepage Vallee * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ #pragma once #include #include class QAbstractItemModel; class QModelIndexWatcherPrivate; /** * This class allows to get events on a QModelIndex from QML. */ -class QModelIndexWatcher : public QObject +class Q_DECL_EXPORT QModelIndexWatcher : public QObject { Q_OBJECT public: explicit QModelIndexWatcher(QObject *parent = nullptr); virtual ~QModelIndexWatcher(); Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) Q_PROPERTY(QModelIndex modelIndex READ modelIndex WRITE setModelIndex NOTIFY indexChanged) Q_PROPERTY(QAbstractItemModel* model READ model NOTIFY validChanged) QModelIndex modelIndex() const; void setModelIndex(const QModelIndex &index); QAbstractItemModel *model() const; bool isValid() const; Q_SIGNALS: void removed(); void moved(); void dataChanged(const QVector& roles); void validChanged(); void indexChanged(); private: QModelIndexWatcherPrivate *d_ptr; Q_DECLARE_PRIVATE(QModelIndexWatcher) }; diff --git a/src/views/indexview.cpp b/src/views/indexview.cpp index 4867a3d..cca29df 100644 --- a/src/views/indexview.cpp +++ b/src/views/indexview.cpp @@ -1,211 +1,209 @@ /*************************************************************************** * Copyright (C) 2018 by Emmanuel Lepage Vallee * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ #include "indexview.h" // Qt #include #include // KQuickItemViews #include #include #include #include /// Make QModelIndexBinder life easier class MIWContextExtension final : public ContextExtension { public: virtual ~MIWContextExtension() {} virtual QVector& propertyNames() const override; virtual QVariant getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const override; IndexViewPrivate *d_ptr; }; class IndexViewPrivate : public QObject { Q_OBJECT public: QQmlComponent *m_pComponent {nullptr}; ContextAdapter *m_pCTX {nullptr}; ContextAdapterFactory *m_pFactory {nullptr}; QQuickItem *m_pItem {nullptr}; MIWContextExtension *m_pExt {nullptr}; QModelIndexWatcher *m_pWatcher {nullptr}; // Helper void initDelegate(); IndexView *q_ptr; public Q_SLOTS: void slotDismiss(); void slotDataChanged(const QVector &roles); }; IndexView::IndexView(QQuickItem *parent) : QQuickItem(parent), d_ptr(new IndexViewPrivate()) { d_ptr->q_ptr = this; d_ptr->m_pWatcher = new QModelIndexWatcher(this); connect(d_ptr->m_pWatcher, &QModelIndexWatcher::removed, d_ptr, &IndexViewPrivate::slotDismiss); connect(d_ptr->m_pWatcher, &QModelIndexWatcher::dataChanged, d_ptr, &IndexViewPrivate::slotDataChanged); } IndexView::~IndexView() { if (d_ptr->m_pItem) delete d_ptr->m_pItem; if (d_ptr->m_pCTX) delete d_ptr->m_pCTX; if (d_ptr->m_pFactory) delete d_ptr->m_pFactory; if (d_ptr->m_pExt) delete d_ptr->m_pExt; delete d_ptr->m_pWatcher; delete d_ptr; } void IndexView::setDelegate(QQmlComponent* delegate) { if (delegate == d_ptr->m_pComponent) return; if (d_ptr->m_pItem) delete d_ptr->m_pItem; d_ptr->m_pComponent = delegate; emit delegateChanged(delegate); d_ptr->initDelegate(); } QQmlComponent* IndexView::delegate() const { return d_ptr->m_pComponent; } QModelIndex IndexView::modelIndex() const { return d_ptr->m_pWatcher->modelIndex(); } QAbstractItemModel *IndexView::model() const { return d_ptr->m_pWatcher->model(); } void IndexView::setModelIndex(const QModelIndex &index) { if (index == modelIndex()) return; // Disconnect old models if (model() && model() != index.model()) d_ptr->slotDismiss(); if (model() != index.model()) { if (!d_ptr->m_pFactory) { d_ptr->m_pExt = new MIWContextExtension (); d_ptr->m_pFactory = new ContextAdapterFactory(); d_ptr->m_pExt->d_ptr = d_ptr; d_ptr->m_pFactory->addContextExtension(d_ptr->m_pExt); } d_ptr->m_pFactory->setModel(const_cast(index.model())); const auto ctx = QQmlEngine::contextForObject(this); if (d_ptr->m_pItem) { delete d_ptr->m_pItem; d_ptr->m_pItem = nullptr; } d_ptr->m_pCTX = d_ptr->m_pFactory->createAdapter(ctx); Q_ASSERT(d_ptr->m_pCTX->context()->parentContext() == ctx); } d_ptr->m_pWatcher->setModelIndex(index); d_ptr->m_pCTX ->setModelIndex(index); d_ptr->initDelegate(); emit indexChanged(); } void IndexViewPrivate::slotDismiss() { if (m_pItem) { delete m_pItem; m_pItem = nullptr; } if (m_pCTX) { delete m_pCTX; m_pCTX = nullptr; } emit q_ptr->indexChanged(); } void IndexViewPrivate::slotDataChanged(const QVector &roles) { m_pCTX->updateRoles(roles); } void IndexViewPrivate::initDelegate() { if (m_pItem || (!m_pComponent) || !q_ptr->modelIndex().isValid()) return; m_pItem = qobject_cast(m_pComponent->create(m_pCTX->context())); // It will happen when the QML itself is invalid if (!m_pItem) return; const auto ctx = QQmlEngine::contextForObject(q_ptr); Q_ASSERT(ctx); ctx->engine()->setObjectOwnership(m_pItem, QQmlEngine::CppOwnership); m_pItem->setParentItem(q_ptr); } - QVector& MIWContextExtension::propertyNames() const { - static QVector ret { "_modelIndexWatcher", }; + static QVector ret { "_modelIndexWatcher", "_contextAdapter" }; return ret; } QVariant MIWContextExtension::getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const { Q_UNUSED(item) Q_UNUSED(index) - Q_ASSERT(!id); - return QVariant::fromValue(d_ptr->m_pWatcher); + return id ? QVariant::fromValue(d_ptr->m_pCTX) : QVariant::fromValue(d_ptr->m_pWatcher); } #include