diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e7e2c7..62cb793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,165 +1,170 @@ 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 + + # Views src/views/comboboxview.cpp src/views/flickable.cpp src/views/hierarchyview.cpp src/views/listview.cpp src/views/treeview.cpp - src/viewport.cpp - src/contextadapterfactory.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 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/plugin.cpp b/src/plugin.cpp index 353a69a..c4ca51f 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,62 +1,64 @@ /*************************************************************************** * 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 "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, "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/views/indexview.cpp b/src/views/indexview.cpp new file mode 100644 index 0000000..f01faec --- /dev/null +++ b/src/views/indexview.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** + * 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 + +class IndexViewPrivate : public QObject +{ + Q_OBJECT +public: + QQmlComponent *m_pComponent {nullptr}; + QAbstractItemModel *m_pModel {nullptr}; + ContextAdapter *m_pCTX {nullptr}; + ContextAdapterFactory *m_pFactory {nullptr}; + QQuickItem *m_pItem {nullptr}; + QPersistentModelIndex m_Index { }; + + // Helper + void initDelegate(); + + IndexView *q_ptr; + +public Q_SLOTS: + void slotDismiss(); + void slotDataChanged(const QModelIndex &tl, const QModelIndex &br, const QVector &roles); + void slotRemoved(const QModelIndex &parent, int first, int last); +}; + +IndexView::IndexView(QQuickItem *parent) : QQuickItem(parent), + d_ptr(new IndexViewPrivate()) +{ + d_ptr->q_ptr = this; +} + +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; + + 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_Index; +} + +void IndexView::setModelIndex(const QModelIndex &index) +{ + if (index == d_ptr->m_Index) + return; + + // Disconnect old models + if (d_ptr->m_pModel && d_ptr->m_pModel != index.model()) + d_ptr->slotDismiss(); + + + if (d_ptr->m_pModel != index.model()) { + + d_ptr->m_pModel = const_cast(index.model()); + + if (!d_ptr->m_pFactory) + d_ptr->m_pFactory = new ContextAdapterFactory(); + + d_ptr->m_pFactory->setModel(d_ptr->m_pModel); + + 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); + + connect(d_ptr->m_pModel, &QAbstractItemModel::destroyed, + d_ptr, &IndexViewPrivate::slotDismiss); + connect(d_ptr->m_pModel, &QAbstractItemModel::dataChanged, + d_ptr, &IndexViewPrivate::slotDataChanged); + connect(d_ptr->m_pModel, &QAbstractItemModel::rowsAboutToBeRemoved, + d_ptr, &IndexViewPrivate::slotRemoved); + } + + d_ptr->m_Index = index; + d_ptr->m_pCTX->setModelIndex(index); + + d_ptr->initDelegate(); +} + +void IndexViewPrivate::slotDismiss() +{ + if (m_pItem) { + delete m_pItem; + m_pItem = nullptr; + } + + disconnect(m_pModel, &QAbstractItemModel::destroyed, + this, &IndexViewPrivate::slotDismiss); + disconnect(m_pModel, &QAbstractItemModel::dataChanged, + this, &IndexViewPrivate::slotDataChanged); + disconnect(m_pModel, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &IndexViewPrivate::slotRemoved); + + if (m_pCTX) { + delete m_pCTX; + m_pCTX = nullptr; + } + + m_pModel = nullptr; + m_Index = QModelIndex(); +} + +void IndexViewPrivate::slotDataChanged(const QModelIndex &tl, const QModelIndex &br, const QVector &roles) +{ + if (tl.parent() != m_Index.parent()) + return; + + if (tl.row() > m_Index.row() || br.row() < m_Index.row()) + return; + + if (tl.column() > m_Index.column() || br.column() < m_Index.column()) + return; + + m_pCTX->updateRoles(roles); +} + +void IndexViewPrivate::slotRemoved(const QModelIndex &parent, int first, int last) +{ + if (parent != m_Index.parent() || !(m_Index.row() >= first && m_Index.row() <= last)) + return; + + slotDismiss(); +} + +void IndexViewPrivate::initDelegate() +{ + if (m_pItem || (!m_pComponent) || !m_Index.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); + volatile auto cc = QQmlEngine::contextForObject(m_pItem); +} + +#include diff --git a/src/views/indexview.h b/src/views/indexview.h new file mode 100644 index 0000000..5a829a1 --- /dev/null +++ b/src/views/indexview.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * 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 IndexViewPrivate; + +/** + * This view has a single delegate instance and display data for a single + * QModelIndex. + * + * This is useful for mobile application pages to get more information out of + * a list item. It allows for more compact list while avoiding the boilerplate + * of having a non-model component. + * + * It can also be used to create the equivalent of "editor widgets" from the + * QtWidgets era. + * + * The IndexView will take the implicit width and height of the component + * unless it is resized or in a managed layout, where it will resize the + * delegate. + */ +class Q_DECL_EXPORT IndexView : public QQuickItem +{ + Q_OBJECT +public: + explicit IndexView(QQuickItem *parent = nullptr); + virtual ~IndexView(); + + Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(QModelIndex modelIndex READ modelIndex WRITE setModelIndex NOTIFY indexChanged) + + virtual void setDelegate(QQmlComponent* delegate); + QQmlComponent* delegate() const; + + QModelIndex modelIndex() const; + void setModelIndex(const QModelIndex &index); + +Q_SIGNALS: + void delegateChanged(QQmlComponent* delegate); + void indexChanged(); + +private: + IndexViewPrivate *d_ptr; +};