From cf488d34a474d7629289ddf12321037e63842a1b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 7 Jul 2017 12:17:37 +1000 Subject: [PATCH] Split layout item registry into two separate registries Instead of relying on forward declared c++ classes from gui in QgsLayoutItemRegistry, instead create a QgsLayoutItemGuiRegistry which handles registration of all the GUI specific behavior relating to layout items. Remove all GUI related code from QgsLayoutItemRegistry. This creates a cleaner split between core/gui code, and given that there'll be a lot of gui specific behavior which needs to be handled by a registry it makes sense to keep this isolated in gui. It also plays nicer with the sip bindings, which can't handle forward declared gui classes in core. --- python/core/layout/qgslayoutitemregistry.sip | 25 +- python/gui/gui_auto.sip | 2 +- .../gui/layout/qgslayoutitemguiregistry.sip | 154 +++++++++++ python/gui/qgsgui.sip | 6 + src/app/layout/qgslayoutdesignerdialog.cpp | 15 +- src/app/layout/qgslayoutdesignerdialog.h | 2 +- src/core/layout/qgslayoutitemregistry.cpp | 19 -- src/core/layout/qgslayoutitemregistry.h | 81 +----- src/gui/CMakeLists.txt | 4 +- src/gui/layout/qgslayoutitemguiregistry.cpp | 87 +++++++ src/gui/layout/qgslayoutitemguiregistry.h | 241 ++++++++++++++++++ .../layout/qgslayoutitemregistryguiutils.cpp | 27 -- .../layout/qgslayoutitemregistryguiutils.h | 50 ---- src/gui/layout/qgslayoutviewtooladditem.cpp | 4 +- src/gui/qgsgui.cpp | 18 +- src/gui/qgsgui.h | 7 + tests/src/core/testqgslayoutitem.cpp | 10 +- tests/src/gui/testqgslayoutview.cpp | 57 +++-- 18 files changed, 580 insertions(+), 229 deletions(-) create mode 100644 python/gui/layout/qgslayoutitemguiregistry.sip create mode 100644 src/gui/layout/qgslayoutitemguiregistry.cpp create mode 100644 src/gui/layout/qgslayoutitemguiregistry.h delete mode 100644 src/gui/layout/qgslayoutitemregistryguiutils.cpp delete mode 100644 src/gui/layout/qgslayoutitemregistryguiutils.h diff --git a/python/core/layout/qgslayoutitemregistry.sip b/python/core/layout/qgslayoutitemregistry.sip index adeb4972b8e..854d914758e 100644 --- a/python/core/layout/qgslayoutitemregistry.sip +++ b/python/core/layout/qgslayoutitemregistry.sip @@ -13,9 +13,13 @@ class QgsLayoutItemAbstractMetadata { %Docstring Stores metadata about one layout item class. + + A companion class, QgsLayoutItemAbstractGuiMetadata, handles the + GUI behavior of QgsLayoutItems. + .. note:: - In C++ you can use QgsSymbolLayerMetadata convenience class. + In C++ you can use QgsLayoutItemMetadata convenience class. .. versionadded:: 3.0 %End @@ -56,13 +60,6 @@ class QgsLayoutItemAbstractMetadata :rtype: QgsLayoutItem %End - virtual QWidget *createItemWidget() /Factory/; -%Docstring - Creates a configuration widget for layout items of this type. Can return None if no configuration GUI is required. - :rtype: QWidget -%End - - virtual void resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving ); %Docstring Resolve paths in the item's ``properties`` (if there are any paths). @@ -79,8 +76,6 @@ class QgsLayoutItemAbstractMetadata - - class QgsLayoutItemRegistry : QObject { %Docstring @@ -89,6 +84,9 @@ class QgsLayoutItemRegistry : QObject QgsLayoutItemRegistry is not usually directly created, but rather accessed through QgsApplication.layoutItemRegistry(). + A companion class, QgsLayoutItemGuiRegistry, handles the GUI behavior + of layout items. + .. versionadded:: 3.0 %End @@ -147,13 +145,6 @@ class QgsLayoutItemRegistry : QObject :rtype: QgsLayoutItem %End - QWidget *createItemWidget( int type ) const /Factory/; -%Docstring - Creates a new instance of a layout item configuration widget for the specified item ``type``. - :rtype: QWidget -%End - - void resolvePaths( int type, QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving ) const; %Docstring Resolve paths in properties of a particular symbol layer. diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 7b80ad8bad1..96a57b60d5c 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -42,7 +42,6 @@ %Include editorwidgets/core/qgseditorwidgetautoconf.sip %Include layertree/qgslayertreeembeddedconfigwidget.sip %Include layertree/qgslayertreeembeddedwidgetregistry.sip -%Include layout/qgslayoutitemregistryguiutils.sip %Include layout/qgslayoutviewmouseevent.sip %Include layout/qgslayoutviewrubberband.sip %Include locator/qgslocatorcontext.sip @@ -278,6 +277,7 @@ %Include layertree/qgslayertreeview.sip %Include layertree/qgslayertreeviewdefaultactions.sip %Include layout/qgslayoutdesignerinterface.sip +%Include layout/qgslayoutitemguiregistry.sip %Include layout/qgslayoutview.sip %Include layout/qgslayoutviewtool.sip %Include layout/qgslayoutviewtooladditem.sip diff --git a/python/gui/layout/qgslayoutitemguiregistry.sip b/python/gui/layout/qgslayoutitemguiregistry.sip new file mode 100644 index 00000000000..059bcd09860 --- /dev/null +++ b/python/gui/layout/qgslayoutitemguiregistry.sip @@ -0,0 +1,154 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/layout/qgslayoutitemguiregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsLayoutItemAbstractGuiMetadata +{ +%Docstring + Stores GUI metadata about one layout item class. + + This is a companion to QgsLayoutItemAbstractMetadata, storing only + the components related to the GUI behavior of a layout item. + +.. note:: + + In C++ you can use QgsLayoutItemGuiMetadata convenience class. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutitemguiregistry.h" +%End + public: + + QgsLayoutItemAbstractGuiMetadata( int type ); +%Docstring + Constructor for QgsLayoutItemAbstractGuiMetadata with the specified class ``type``. +%End + + virtual ~QgsLayoutItemAbstractGuiMetadata(); + + int type() const; +%Docstring + Returns the unique item type code for the layout item class. + :rtype: int +%End + + virtual QIcon creationIcon() const; +%Docstring + Returns an icon representing creation of the layout item type. + :rtype: QIcon +%End + + virtual QWidget *createItemWidget() /Factory/; +%Docstring + Creates a configuration widget for layout items of this type. Can return None if no configuration GUI is required. + :rtype: QWidget +%End + + virtual QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) /Factory/; +%Docstring + Creates a rubber band for use when creating layout items of this type. Can return None if no rubber band + should be created. The default behavior is to create a rectangular rubber band. + :rtype: QgsLayoutViewRubberBand +%End + +}; + + + + +class QgsLayoutItemGuiRegistry : QObject +{ +%Docstring + Registry of available layout item GUI behavior. + + QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed through + QgsGui.layoutItemGuiRegistry(). + + This acts as a companion to QgsLayoutItemRegistry, handling only + the components related to the GUI behavior of layout items. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutitemguiregistry.h" +%End + public: + + QgsLayoutItemGuiRegistry( QObject *parent = 0 ); +%Docstring + Creates a new empty item GUI registry. + + QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed through + QgsGui.layoutItemGuiRegistry(). + +.. seealso:: populate() +%End + + ~QgsLayoutItemGuiRegistry(); + + bool populate(); +%Docstring + Populates the registry with standard item types. If called on a non-empty registry + then this will have no effect and will return false. + :rtype: bool +%End + + + QgsLayoutItemAbstractGuiMetadata *itemMetadata( int type ) const; +%Docstring + Returns the metadata for the specified item ``type``. Returns None if + a corresponding type was not found in the registry. + :rtype: QgsLayoutItemAbstractGuiMetadata +%End + + bool addLayoutItemGuiMetadata( QgsLayoutItemAbstractGuiMetadata *metadata /Transfer/ ); +%Docstring + Registers the gui metadata for a new layout item type. Takes ownership of the metadata instance. + :rtype: bool +%End + + QWidget *createItemWidget( int type ) const /Factory/; +%Docstring + Creates a new instance of a layout item configuration widget for the specified item ``type``. + :rtype: QWidget +%End + + + QList< int > itemTypes() const; +%Docstring + Returns a list of available item types handled by the registry. + :rtype: list of int +%End + + signals: + + void typeAdded( int type ); +%Docstring + Emitted whenever a new item type is added to the registry, with the specified + ``type``. +%End + + private: + QgsLayoutItemGuiRegistry( const QgsLayoutItemGuiRegistry &rh ); +}; + + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/layout/qgslayoutitemguiregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/qgsgui.sip b/python/gui/qgsgui.sip index 7c32f1227f1..f8ba363606a 100644 --- a/python/gui/qgsgui.sip +++ b/python/gui/qgsgui.sip @@ -56,6 +56,12 @@ class QgsGui :rtype: QgsMapLayerActionRegistry %End + static QgsLayoutItemGuiRegistry *layoutItemGuiRegistry(); +%Docstring + Returns the global layout item GUI registry, used for registering the GUI behavior of layout items. + :rtype: QgsLayoutItemGuiRegistry +%End + ~QgsGui(); private: diff --git a/src/app/layout/qgslayoutdesignerdialog.cpp b/src/app/layout/qgslayoutdesignerdialog.cpp index 95f8662b5c2..2ec0ea12cdf 100644 --- a/src/app/layout/qgslayoutdesignerdialog.cpp +++ b/src/app/layout/qgslayoutdesignerdialog.cpp @@ -26,6 +26,8 @@ #include "qgslayoutviewtoolpan.h" #include "qgslayoutviewtoolzoom.h" #include "qgslayoutviewtoolselect.h" +#include "qgsgui.h" +#include "qgslayoutitemguiregistry.h" QgsAppLayoutDesignerInterface::QgsAppLayoutDesignerInterface( QgsLayoutDesignerDialog *dialog ) : QgsLayoutDesignerInterface( dialog ) @@ -89,14 +91,12 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla connect( mActionClose, &QAction::triggered, this, &QWidget::close ); // populate with initial items... - QMap< int, QString> types = QgsApplication::layoutItemRegistry()->itemTypes(); - QMap< int, QString>::const_iterator typeIt = types.constBegin(); - for ( ; typeIt != types.constEnd(); ++typeIt ) + Q_FOREACH ( int type, QgsGui::layoutItemGuiRegistry()->itemTypes() ) { - itemTypeAdded( typeIt.key(), typeIt.value() ); + itemTypeAdded( type ); } //..and listen out for new item types - connect( QgsApplication::layoutItemRegistry(), &QgsLayoutItemRegistry::typeAdded, this, &QgsLayoutDesignerDialog::itemTypeAdded ); + connect( QgsGui::layoutItemGuiRegistry(), &QgsLayoutItemGuiRegistry::typeAdded, this, &QgsLayoutDesignerDialog::itemTypeAdded ); mAddItemTool = new QgsLayoutViewToolAddItem( mView ); mPanTool = new QgsLayoutViewToolPan( mView ); @@ -192,14 +192,15 @@ void QgsLayoutDesignerDialog::closeEvent( QCloseEvent * ) saveWindowState(); } -void QgsLayoutDesignerDialog::itemTypeAdded( int type, const QString &name ) +void QgsLayoutDesignerDialog::itemTypeAdded( int type ) { + QString name = QgsApplication::layoutItemRegistry()->itemMetadata( type )->visibleName(); // update UI for new item type QAction *action = new QAction( tr( "Add %1" ).arg( name ), this ); action->setToolTip( tr( "Adds a new %1 to the layout" ).arg( name ) ); action->setCheckable( true ); action->setData( type ); - action->setIcon( QgsApplication::layoutItemRegistry()->itemMetadata( type )->icon() ); + action->setIcon( QgsGui::layoutItemGuiRegistry()->itemMetadata( type )->creationIcon() ); mToolsActionGroup->addAction( action ); mItemMenu->addAction( action ); mToolsToolbar->addAction( action ); diff --git a/src/app/layout/qgslayoutdesignerdialog.h b/src/app/layout/qgslayoutdesignerdialog.h index f5c739f028b..e1df1901724 100644 --- a/src/app/layout/qgslayoutdesignerdialog.h +++ b/src/app/layout/qgslayoutdesignerdialog.h @@ -115,7 +115,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner private slots: - void itemTypeAdded( int type, const QString &name ); + void itemTypeAdded( int type ); private: diff --git a/src/core/layout/qgslayoutitemregistry.cpp b/src/core/layout/qgslayoutitemregistry.cpp index d93311b4bdf..6bed127a26c 100644 --- a/src/core/layout/qgslayoutitemregistry.cpp +++ b/src/core/layout/qgslayoutitemregistry.cpp @@ -65,25 +65,6 @@ QgsLayoutItem *QgsLayoutItemRegistry::createItem( int type, QgsLayout *layout, c return mMetadata[type]->createItem( layout, properties ); } -QWidget *QgsLayoutItemRegistry::createItemWidget( int type ) const -{ - if ( !mMetadata.contains( type ) ) - return nullptr; - - return mMetadata[type]->createItemWidget(); -} - -QgsLayoutViewRubberBand *QgsLayoutItemRegistry::createItemRubberBand( int type, QgsLayoutView *view ) const -{ - if ( mRubberBandFunctions.contains( type ) ) - return mRubberBandFunctions.value( type )( view ); - - if ( !mMetadata.contains( type ) ) - return nullptr; - - return mMetadata[type]->createRubberBand( view ); -} - void QgsLayoutItemRegistry::resolvePaths( int type, QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving ) const { if ( !mMetadata.contains( type ) ) diff --git a/src/core/layout/qgslayoutitemregistry.h b/src/core/layout/qgslayoutitemregistry.h index d51986acaaa..0d2eed98b31 100644 --- a/src/core/layout/qgslayoutitemregistry.h +++ b/src/core/layout/qgslayoutitemregistry.h @@ -29,12 +29,15 @@ class QgsLayout; class QgsLayoutView; class QgsLayoutItem; -class QgsLayoutViewRubberBand; /** * \ingroup core * \brief Stores metadata about one layout item class. - * \note In C++ you can use QgsSymbolLayerMetadata convenience class. + * + * A companion class, QgsLayoutItemAbstractGuiMetadata, handles the + * GUI behavior of QgsLayoutItems. + * + * \note In C++ you can use QgsLayoutItemMetadata convenience class. * \since QGIS 3.0 */ class CORE_EXPORT QgsLayoutItemAbstractMetadata @@ -72,19 +75,6 @@ class CORE_EXPORT QgsLayoutItemAbstractMetadata */ virtual QgsLayoutItem *createItem( QgsLayout *layout, const QVariantMap &properties ) = 0 SIP_FACTORY; - /** - * Creates a configuration widget for layout items of this type. Can return nullptr if no configuration GUI is required. - */ - virtual QWidget *createItemWidget() SIP_FACTORY { return nullptr; } - - /** - * Creates a rubber band for use when creating layout items of this type. Can return nullptr if no rubber band - * should be created. - * \note not available in Python bindings. Python item subclasses must use QgsLayoutItemRegistryGuiUtils - * to override the default rubber band creation function. - */ - virtual QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) SIP_SKIP { Q_UNUSED( view ); return nullptr; } - /** * Resolve paths in the item's \a properties (if there are any paths). * When \a saving is true, paths are converted from absolute to relative, @@ -108,12 +98,6 @@ class CORE_EXPORT QgsLayoutItemAbstractMetadata //! Layout item creation function typedef std::function QgsLayoutItemCreateFunc SIP_SKIP; -//! Layout item configuration widget creation function -typedef std::function QgsLayoutItemWidgetFunc SIP_SKIP; - -//! Layout rubber band creation function -typedef std::function QgsLayoutItemRubberBandFunc SIP_SKIP; - //! Layout item path resolver function typedef std::function QgsLayoutItemPathResolverFunc SIP_SKIP; @@ -131,17 +115,14 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata /** * Constructor for QgsLayoutItemMetadata with the specified class \a type - * and \a visibleName, and function pointers for the various item and - * configuration widget creation functions. + * and \a visibleName, and function pointers for the various item creation functions. */ QgsLayoutItemMetadata( int type, const QString &visibleName, const QIcon &icon, QgsLayoutItemCreateFunc pfCreate, - QgsLayoutItemPathResolverFunc pfPathResolver = nullptr, - QgsLayoutItemWidgetFunc pfWidget = nullptr ) + QgsLayoutItemPathResolverFunc pfPathResolver = nullptr ) : QgsLayoutItemAbstractMetadata( type, visibleName ) , mIcon( icon ) , mCreateFunc( pfCreate ) - , mWidgetFunc( pfWidget ) , mPathResolverFunc( pfPathResolver ) {} @@ -150,39 +131,13 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata */ QgsLayoutItemCreateFunc createFunction() const { return mCreateFunc; } - /** - * Returns the classes' configuration widget creation function. - * \see setWidgetFunction() - */ - QgsLayoutItemWidgetFunc widgetFunction() const { return mWidgetFunc; } - /** * Returns the classes' path resolver function. */ QgsLayoutItemPathResolverFunc pathResolverFunction() const { return mPathResolverFunc; } - /** - * Sets the classes' configuration widget creation \a function. - * \see widgetFunction() - */ - void setWidgetFunction( QgsLayoutItemWidgetFunc function ) { mWidgetFunc = function; } - - /** - * Returns the classes' rubber band creation function. - * \see setRubberBandCreationFunction() - */ - QgsLayoutItemRubberBandFunc rubberBandCreationFunction() const { return mRubberBandFunc; } - - /** - * Sets the classes' rubber band creation \a function. - * \see rubberBandCreationFunction() - */ - void setRubberBandCreationFunction( QgsLayoutItemRubberBandFunc function ) { mRubberBandFunc = function; } - QIcon icon() const override { return mIcon.isNull() ? QgsLayoutItemAbstractMetadata::icon() : mIcon; } QgsLayoutItem *createItem( QgsLayout *layout, const QVariantMap &properties ) override { return mCreateFunc ? mCreateFunc( layout, properties ) : nullptr; } - QWidget *createItemWidget() override { return mWidgetFunc ? mWidgetFunc() : nullptr; } - QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) override { return mRubberBandFunc ? mRubberBandFunc( view ) : nullptr; } void resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving ) override { @@ -193,8 +148,6 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata protected: QIcon mIcon; QgsLayoutItemCreateFunc mCreateFunc = nullptr; - QgsLayoutItemWidgetFunc mWidgetFunc = nullptr; - QgsLayoutItemRubberBandFunc mRubberBandFunc = nullptr; QgsLayoutItemPathResolverFunc mPathResolverFunc = nullptr; }; @@ -211,6 +164,9 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata * QgsLayoutItemRegistry is not usually directly created, but rather accessed through * QgsApplication::layoutItemRegistry(). * + * A companion class, QgsLayoutItemGuiRegistry, handles the GUI behavior + * of layout items. + * * \since QGIS 3.0 */ class CORE_EXPORT QgsLayoutItemRegistry : public QObject @@ -251,7 +207,7 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject //! QgsLayoutItemRegistry cannot be copied. QgsLayoutItemRegistry( const QgsLayoutItemRegistry &rh ) = delete; - //! QgsLayoutItemRegistryQgsLayoutItemRegistry cannot be copied. + //! QgsLayoutItemRegistry cannot be copied. QgsLayoutItemRegistry &operator=( const QgsLayoutItemRegistry &rh ) = delete; /** @@ -270,17 +226,6 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject */ QgsLayoutItem *createItem( int type, QgsLayout *layout, const QVariantMap &properties = QVariantMap() ) const SIP_FACTORY; - /** - * Creates a new instance of a layout item configuration widget for the specified item \a type. - */ - QWidget *createItemWidget( int type ) const SIP_FACTORY; - - /** - * Creates a new rubber band item for the specified item \a type and destination \a view. - * \note not available from Python bindings - */ - QgsLayoutViewRubberBand *createItemRubberBand( int type, QgsLayoutView *view ) const SIP_SKIP; - /** * Resolve paths in properties of a particular symbol layer. * This normally means converting relative paths to absolute paths when loading @@ -308,10 +253,6 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject QMap mMetadata; - QMap mRubberBandFunctions; - - friend class QgsLayoutItemRegistryGuiUtils; - }; #ifndef SIP_RUN diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 8e0950ca523..f281502891b 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -158,7 +158,7 @@ SET(QGIS_GUI_SRCS layertree/qgslayertreeview.cpp layertree/qgslayertreeviewdefaultactions.cpp - layout/qgslayoutitemregistryguiutils.cpp + layout/qgslayoutitemguiregistry.cpp layout/qgslayoutview.cpp layout/qgslayoutviewmouseevent.cpp layout/qgslayoutviewrubberband.cpp @@ -633,6 +633,7 @@ SET(QGIS_GUI_MOC_HDRS layertree/qgslayertreeviewdefaultactions.h layout/qgslayoutdesignerinterface.h + layout/qgslayoutitemguiregistry.h layout/qgslayoutview.h layout/qgslayoutviewtool.h layout/qgslayoutviewtooladditem.h @@ -735,7 +736,6 @@ SET(QGIS_GUI_HDRS layertree/qgslayertreeembeddedconfigwidget.h layertree/qgslayertreeembeddedwidgetregistry.h - layout/qgslayoutitemregistryguiutils.h layout/qgslayoutviewmouseevent.h layout/qgslayoutviewrubberband.h diff --git a/src/gui/layout/qgslayoutitemguiregistry.cpp b/src/gui/layout/qgslayoutitemguiregistry.cpp new file mode 100644 index 00000000000..f31b6ba2500 --- /dev/null +++ b/src/gui/layout/qgslayoutitemguiregistry.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + qgslayoutitemregistry.cpp + ------------------------- + begin : June 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgslayoutitemguiregistry.h" +#include "qgslayoutviewrubberband.h" +#include + + +QgsLayoutViewRubberBand *QgsLayoutItemAbstractGuiMetadata::createRubberBand( QgsLayoutView *view ) +{ + return new QgsLayoutViewRectangularRubberBand( view ); +} + + +QgsLayoutItemGuiRegistry::QgsLayoutItemGuiRegistry( QObject *parent ) + : QObject( parent ) +{ +} + +QgsLayoutItemGuiRegistry::~QgsLayoutItemGuiRegistry() +{ + qDeleteAll( mMetadata ); +} + +bool QgsLayoutItemGuiRegistry::populate() +{ + if ( !mMetadata.isEmpty() ) + return false; + + // add temporary item to register + auto createRubberBand = ( []( QgsLayoutView * view )->QgsLayoutViewRubberBand * + { + return new QgsLayoutViewRectangularRubberBand( view ); + } ); + + addLayoutItemGuiMetadata( new QgsLayoutItemGuiMetadata( 101, QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddLabel.svg" ) ), nullptr, createRubberBand ) ); + return true; +} + +QgsLayoutItemAbstractGuiMetadata *QgsLayoutItemGuiRegistry::itemMetadata( int type ) const +{ + return mMetadata.value( type ); +} + +bool QgsLayoutItemGuiRegistry::addLayoutItemGuiMetadata( QgsLayoutItemAbstractGuiMetadata *metadata ) +{ + if ( !metadata || mMetadata.contains( metadata->type() ) ) + return false; + + mMetadata[metadata->type()] = metadata; + emit typeAdded( metadata->type() ); + return true; +} + +QWidget *QgsLayoutItemGuiRegistry::createItemWidget( int type ) const +{ + if ( !mMetadata.contains( type ) ) + return nullptr; + + return mMetadata[type]->createItemWidget(); +} + +QgsLayoutViewRubberBand *QgsLayoutItemGuiRegistry::createItemRubberBand( int type, QgsLayoutView *view ) const +{ + if ( !mMetadata.contains( type ) ) + return nullptr; + + return mMetadata[type]->createRubberBand( view ); +} + +QList QgsLayoutItemGuiRegistry::itemTypes() const +{ + return mMetadata.keys(); +} diff --git a/src/gui/layout/qgslayoutitemguiregistry.h b/src/gui/layout/qgslayoutitemguiregistry.h new file mode 100644 index 00000000000..ca6cc875a70 --- /dev/null +++ b/src/gui/layout/qgslayoutitemguiregistry.h @@ -0,0 +1,241 @@ +/*************************************************************************** + qgslayoutitemguiregistry.h + -------------------------- + begin : June 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSLAYOUTITEMGUIREGISTRY_H +#define QGSLAYOUTITEMGUIREGISTRY_H + +#include "qgis_gui.h" +#include "qgis_sip.h" +#include "qgsapplication.h" +#include "qgspathresolver.h" +#include //for QGraphicsItem::UserType +#include +#include + +#include "qgslayoutitem.h" // temporary + +class QgsLayout; +class QgsLayoutView; +class QgsLayoutItem; +class QgsLayoutViewRubberBand; + +/** + * \ingroup gui + * \brief Stores GUI metadata about one layout item class. + * + * This is a companion to QgsLayoutItemAbstractMetadata, storing only + * the components related to the GUI behavior of a layout item. + * + * \note In C++ you can use QgsLayoutItemGuiMetadata convenience class. + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsLayoutItemAbstractGuiMetadata +{ + public: + + /** + * Constructor for QgsLayoutItemAbstractGuiMetadata with the specified class \a type. + */ + QgsLayoutItemAbstractGuiMetadata( int type ) + : mType( type ) + {} + + virtual ~QgsLayoutItemAbstractGuiMetadata() = default; + + /** + * Returns the unique item type code for the layout item class. + */ + int type() const { return mType; } + + /** + * Returns an icon representing creation of the layout item type. + */ + virtual QIcon creationIcon() const { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicRectangle.svg" ) ); } + + /** + * Creates a configuration widget for layout items of this type. Can return nullptr if no configuration GUI is required. + */ + virtual QWidget *createItemWidget() SIP_FACTORY { return nullptr; } + + /** + * Creates a rubber band for use when creating layout items of this type. Can return nullptr if no rubber band + * should be created. The default behavior is to create a rectangular rubber band. + */ + virtual QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) SIP_FACTORY; + + private: + + int mType = -1; +}; + +//! Layout item configuration widget creation function +typedef std::function QgsLayoutItemWidgetFunc SIP_SKIP; + +//! Layout rubber band creation function +typedef std::function QgsLayoutItemRubberBandFunc SIP_SKIP; + +#ifndef SIP_RUN + +/** + * \ingroup gui + * Convenience metadata class that uses static functions to handle layout item GUI behavior. + * \since QGIS 3.0 + * \note not available in Python bindings + */ +class CORE_EXPORT QgsLayoutItemGuiMetadata : public QgsLayoutItemAbstractGuiMetadata +{ + public: + + /** + * Constructor for QgsLayoutItemGuiMetadata with the specified class \a type + * and \a creationIcon, and function pointers for the various + * configuration widget creation functions. + */ + QgsLayoutItemGuiMetadata( int type, const QIcon &creationIcon, + QgsLayoutItemWidgetFunc pfWidget = nullptr, + QgsLayoutItemRubberBandFunc pfRubberBand = nullptr ) + : QgsLayoutItemAbstractGuiMetadata( type ) + , mIcon( creationIcon ) + , mWidgetFunc( pfWidget ) + , mRubberBandFunc( pfRubberBand ) + {} + + /** + * Returns the classes' configuration widget creation function. + * \see setWidgetFunction() + */ + QgsLayoutItemWidgetFunc widgetFunction() const { return mWidgetFunc; } + + /** + * Sets the classes' configuration widget creation \a function. + * \see widgetFunction() + */ + void setWidgetFunction( QgsLayoutItemWidgetFunc function ) { mWidgetFunc = function; } + + /** + * Returns the classes' rubber band creation function. + * \see setRubberBandCreationFunction() + */ + QgsLayoutItemRubberBandFunc rubberBandCreationFunction() const { return mRubberBandFunc; } + + /** + * Sets the classes' rubber band creation \a function. + * \see rubberBandCreationFunction() + */ + void setRubberBandCreationFunction( QgsLayoutItemRubberBandFunc function ) { mRubberBandFunc = function; } + + QIcon creationIcon() const override { return mIcon.isNull() ? QgsLayoutItemAbstractGuiMetadata::creationIcon() : mIcon; } + QWidget *createItemWidget() override { return mWidgetFunc ? mWidgetFunc() : nullptr; } + QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) override { return mRubberBandFunc ? mRubberBandFunc( view ) : nullptr; } + + protected: + QIcon mIcon; + QgsLayoutItemWidgetFunc mWidgetFunc = nullptr; + QgsLayoutItemRubberBandFunc mRubberBandFunc = nullptr; + +}; + +#endif + +/** + * \ingroup core + * \class QgsLayoutItemGuiRegistry + * \brief Registry of available layout item GUI behavior. + * + * QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed through + * QgsGui::layoutItemGuiRegistry(). + * + * This acts as a companion to QgsLayoutItemRegistry, handling only + * the components related to the GUI behavior of layout items. + * + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsLayoutItemGuiRegistry : public QObject +{ + Q_OBJECT + + public: + + /** + * Creates a new empty item GUI registry. + * + * QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed through + * QgsGui::layoutItemGuiRegistry(). + * + * \see populate() + */ + QgsLayoutItemGuiRegistry( QObject *parent = nullptr ); + + ~QgsLayoutItemGuiRegistry(); + + /** + * Populates the registry with standard item types. If called on a non-empty registry + * then this will have no effect and will return false. + */ + bool populate(); + + //! QgsLayoutItemGuiRegistry cannot be copied. + QgsLayoutItemGuiRegistry( const QgsLayoutItemGuiRegistry &rh ) = delete; + //! QgsLayoutItemGuiRegistry cannot be copied. + QgsLayoutItemGuiRegistry &operator=( const QgsLayoutItemGuiRegistry &rh ) = delete; + + /** + * Returns the metadata for the specified item \a type. Returns nullptr if + * a corresponding type was not found in the registry. + */ + QgsLayoutItemAbstractGuiMetadata *itemMetadata( int type ) const; + + /** + * Registers the gui metadata for a new layout item type. Takes ownership of the metadata instance. + */ + bool addLayoutItemGuiMetadata( QgsLayoutItemAbstractGuiMetadata *metadata SIP_TRANSFER ); + + /** + * Creates a new instance of a layout item configuration widget for the specified item \a type. + */ + QWidget *createItemWidget( int type ) const SIP_FACTORY; + + /** + * Creates a new rubber band item for the specified item \a type and destination \a view. + * \note not available from Python bindings + */ + QgsLayoutViewRubberBand *createItemRubberBand( int type, QgsLayoutView *view ) const SIP_SKIP; + + /** + * Returns a list of available item types handled by the registry. + */ + QList< int > itemTypes() const; + + signals: + + /** + * Emitted whenever a new item type is added to the registry, with the specified + * \a type. + */ + void typeAdded( int type ); + + private: +#ifdef SIP_RUN + QgsLayoutItemGuiRegistry( const QgsLayoutItemGuiRegistry &rh ); +#endif + + QMap mMetadata; + +}; + +#endif //QGSLAYOUTITEMGUIREGISTRY_H + + + diff --git a/src/gui/layout/qgslayoutitemregistryguiutils.cpp b/src/gui/layout/qgslayoutitemregistryguiutils.cpp deleted file mode 100644 index a63b2dd75f8..00000000000 --- a/src/gui/layout/qgslayoutitemregistryguiutils.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*************************************************************************** - qgslayoutitemregistryguiutils.h - ------------------------------- - Date : July 2017 - Copyright : (C) 2017 Nyall Dawson - Email : nyall dot dawson at gmail dot com - *************************************************************************** - * * - * 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - - -#include "qgslayoutitemregistryguiutils.h" -#include "qgslayoutviewrubberband.h" - -void QgsLayoutItemRegistryGuiUtils::setItemRubberBandPrototype( int type, QgsLayoutViewRubberBand *prototype ) -{ - auto create = [prototype]( QgsLayoutView * view )->QgsLayoutViewRubberBand * - { - return prototype->create( view ); - }; - QgsApplication::layoutItemRegistry()->mRubberBandFunctions.insert( type, create ); -} diff --git a/src/gui/layout/qgslayoutitemregistryguiutils.h b/src/gui/layout/qgslayoutitemregistryguiutils.h deleted file mode 100644 index 26d899f8cf6..00000000000 --- a/src/gui/layout/qgslayoutitemregistryguiutils.h +++ /dev/null @@ -1,50 +0,0 @@ -/*************************************************************************** - qgslayoutitemregistryguiutils.h - ------------------------------- - Date : July 2017 - Copyright : (C) 2017 Nyall Dawson - Email : nyall dot dawson at gmail dot com - *************************************************************************** - * * - * 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef QGSLAYOUTITEMREGISTRYGUIUTILS_H -#define QGSLAYOUTITEMREGISTRYGUIUTILS_H - -#include "qgis_gui.h" -#include "qgis_sip.h" -#include "qgslayoutitemregistry.h" - -class QgsLayoutViewRubberBand; - -/** - * \ingroup gui - * A group of static utilities for working with the gui based portions of - * QgsLayoutItemRegistry. - * - * This class is designed to allow Python item subclasses to override the - * default GUI based QgsLayoutItemAbstractMetadata methods, which - * cannot be directly overridden from Python subclasses. - * - * \since QGIS 3.0 - */ -class GUI_EXPORT QgsLayoutItemRegistryGuiUtils -{ - public: - - /** - * Sets a \a prototype for the rubber bands for the layout item with specified \a type. - * Python subclasses of QgsLayoutItem must call this method to register their prototypes, - * as the usual c++ QgsLayoutItemAbstractMetadata are not accessible via the Python bindings. - */ - static void setItemRubberBandPrototype( int type, QgsLayoutViewRubberBand *prototype SIP_TRANSFER ); - - -}; - -#endif // QGSLAYOUTITEMREGISTRYGUIUTILS_H diff --git a/src/gui/layout/qgslayoutviewtooladditem.cpp b/src/gui/layout/qgslayoutviewtooladditem.cpp index e4405610c55..497924aa1a1 100644 --- a/src/gui/layout/qgslayoutviewtooladditem.cpp +++ b/src/gui/layout/qgslayoutviewtooladditem.cpp @@ -22,6 +22,8 @@ #include "qgslayoutviewmouseevent.h" #include "qgslogger.h" #include "qgslayoutviewrubberband.h" +#include "qgsgui.h" +#include "qgslayoutitemguiregistry.h" #include #include #include @@ -49,7 +51,7 @@ void QgsLayoutViewToolAddItem::layoutPressEvent( QgsLayoutViewMouseEvent *event mDrawing = true; mMousePressStartPos = event->pos(); - mRubberBand.reset( QgsApplication::layoutItemRegistry()->createItemRubberBand( mItemType, view() ) ); + mRubberBand.reset( QgsGui::layoutItemGuiRegistry()->createItemRubberBand( mItemType, view() ) ); if ( mRubberBand ) { mRubberBand->start( event->layoutPoint(), event->modifiers() ); diff --git a/src/gui/qgsgui.cpp b/src/gui/qgsgui.cpp index eac45a2a482..0227af59b69 100644 --- a/src/gui/qgsgui.cpp +++ b/src/gui/qgsgui.cpp @@ -20,6 +20,7 @@ #include "qgslayertreeembeddedwidgetregistry.h" #include "qgsmaplayeractionregistry.h" #include "qgslayoutitemregistry.h" +#include "qgslayoutitemguiregistry.h" #include "qgslayoutviewrubberband.h" #ifdef Q_OS_MACX #include "qgsmacnative.h" @@ -59,8 +60,14 @@ QgsMapLayerActionRegistry *QgsGui::mapLayerActionRegistry() return instance()->mMapLayerActionRegistry; } +QgsLayoutItemGuiRegistry *QgsGui::layoutItemGuiRegistry() +{ + return instance()->mLayoutItemGuiRegistry; +} + QgsGui::~QgsGui() { + delete mLayoutItemGuiRegistry; delete mLayerTreeEmbeddedWidgetRegistry; delete mEditorWidgetRegistry; delete mMapLayerActionRegistry; @@ -80,13 +87,6 @@ QgsGui::QgsGui() mShortcutsManager = new QgsShortcutsManager(); mLayerTreeEmbeddedWidgetRegistry = new QgsLayerTreeEmbeddedWidgetRegistry(); mMapLayerActionRegistry = new QgsMapLayerActionRegistry(); - - - QgsLayoutItemAbstractMetadata *abstractMetadata = QgsApplication::layoutItemRegistry()->itemMetadata( 101 ); - QgsLayoutItemMetadata *metadata = dynamic_cast( abstractMetadata ); - metadata->setRubberBandCreationFunction( []( QgsLayoutView * view )->QgsLayoutViewRubberBand * - { - return new QgsLayoutViewRectangularRubberBand( view ); - } ); - + mLayoutItemGuiRegistry = new QgsLayoutItemGuiRegistry(); + mLayoutItemGuiRegistry->populate(); } diff --git a/src/gui/qgsgui.h b/src/gui/qgsgui.h index 71629780787..794e8ef79c1 100644 --- a/src/gui/qgsgui.h +++ b/src/gui/qgsgui.h @@ -26,6 +26,7 @@ class QgsShortcutsManager; class QgsLayerTreeEmbeddedWidgetRegistry; class QgsMapLayerActionRegistry; class QgsNative; +class QgsLayoutItemGuiRegistry; /** * \ingroup gui @@ -75,6 +76,11 @@ class GUI_EXPORT QgsGui */ static QgsMapLayerActionRegistry *mapLayerActionRegistry(); + /** + * Returns the global layout item GUI registry, used for registering the GUI behavior of layout items. + */ + static QgsLayoutItemGuiRegistry *layoutItemGuiRegistry(); + ~QgsGui(); private: @@ -86,6 +92,7 @@ class GUI_EXPORT QgsGui QgsShortcutsManager *mShortcutsManager = nullptr; QgsLayerTreeEmbeddedWidgetRegistry *mLayerTreeEmbeddedWidgetRegistry = nullptr; QgsMapLayerActionRegistry *mMapLayerActionRegistry = nullptr; + QgsLayoutItemGuiRegistry *mLayoutItemGuiRegistry = nullptr; #ifdef SIP_RUN QgsGui( const QgsGui &other ); diff --git a/tests/src/core/testqgslayoutitem.cpp b/tests/src/core/testqgslayoutitem.cpp index ea9a765a039..ea70cf70439 100644 --- a/tests/src/core/testqgslayoutitem.cpp +++ b/tests/src/core/testqgslayoutitem.cpp @@ -115,16 +115,11 @@ void TestQgsLayoutItem::registry() QVERIFY( !registry.itemMetadata( -1 ) ); QVERIFY( registry.itemTypes().isEmpty() ); QVERIFY( !registry.createItem( 1, nullptr ) ); - QVERIFY( !registry.createItemWidget( 1 ) ); auto create = []( QgsLayout * layout, const QVariantMap & )->QgsLayoutItem* { return new TestItem( layout ); }; - auto createWidget = []()->QWidget* - { - return new QWidget(); - }; auto resolve = []( QVariantMap & props, const QgsPathResolver &, bool ) { props.clear(); @@ -132,7 +127,7 @@ void TestQgsLayoutItem::registry() QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemRegistry::typeAdded ); - QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), QIcon(), create, resolve, createWidget ); + QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), QIcon(), create, resolve ); QVERIFY( registry.addLayoutItemType( metadata ) ); QCOMPARE( spyTypeAdded.count(), 1 ); QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), 2 ); @@ -150,9 +145,6 @@ void TestQgsLayoutItem::registry() QVERIFY( item ); QVERIFY( dynamic_cast< TestItem *>( item ) ); delete item; - QWidget *config = registry.createItemWidget( 2 ); - QVERIFY( config ); - delete config; QVariantMap props; props.insert( QStringLiteral( "a" ), 5 ); registry.resolvePaths( 1, props, QgsPathResolver(), true ); diff --git a/tests/src/gui/testqgslayoutview.cpp b/tests/src/gui/testqgslayoutview.cpp index abf78757837..e56cec1bdbe 100644 --- a/tests/src/gui/testqgslayoutview.cpp +++ b/tests/src/gui/testqgslayoutview.cpp @@ -20,8 +20,10 @@ #include "qgslayoutviewmouseevent.h" #include "qgslayoutitem.h" #include "qgslayoutviewrubberband.h" -#include "qgslayoutitemregistryguiutils.h" +#include "qgslayoutitemregistry.h" +#include "qgslayoutitemguiregistry.h" #include "qgstestutils.h" +#include "qgsgui.h" #include class TestQgsLayoutView: public QObject @@ -35,7 +37,7 @@ class TestQgsLayoutView: public QObject void basic(); void tool(); void events(); - void registryUtils(); + void guiRegistry(); void rubberBand(); private: @@ -246,12 +248,22 @@ QString mReport; bool renderCheck( QString testName, QImage &image, int mismatchCount ); -void TestQgsLayoutView::registryUtils() +void TestQgsLayoutView::guiRegistry() { + // test QgsLayoutItemGuiRegistry + QgsLayoutItemGuiRegistry registry; + + // empty registry + QVERIFY( !registry.itemMetadata( -1 ) ); + QVERIFY( registry.itemTypes().isEmpty() ); + QVERIFY( !registry.createItemWidget( 1 ) ); + + QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemGuiRegistry::typeAdded ); + // add a dummy item to registry - auto create = []( QgsLayout * layout, const QVariantMap & )->QgsLayoutItem* + auto createWidget = []()->QWidget* { - return new TestItem( layout ); + return new QWidget(); }; auto createRubberBand = []( QgsLayoutView * view )->QgsLayoutViewRubberBand * @@ -259,25 +271,38 @@ void TestQgsLayoutView::registryUtils() return new QgsLayoutViewRectangularRubberBand( view ); }; - QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), QIcon(), create ); - metadata->setRubberBandCreationFunction( createRubberBand ); - QVERIFY( QgsApplication::layoutItemRegistry()->addLayoutItemType( metadata ) ); + QgsLayoutItemGuiMetadata *metadata = new QgsLayoutItemGuiMetadata( 2, QIcon(), createWidget, createRubberBand ); + QVERIFY( registry.addLayoutItemGuiMetadata( metadata ) ); + QCOMPARE( spyTypeAdded.count(), 1 ); + QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), 2 ); + // duplicate type id + QVERIFY( !registry.addLayoutItemGuiMetadata( metadata ) ); + QCOMPARE( spyTypeAdded.count(), 1 ); + + //retrieve metadata + QVERIFY( !registry.itemMetadata( -1 ) ); + QVERIFY( registry.itemMetadata( 2 ) ); + QCOMPARE( registry.itemTypes().count(), 1 ); + QCOMPARE( registry.itemTypes().value( 0 ), 2 ); + + QWidget *widget = registry.createItemWidget( 2 ); + QVERIFY( widget ); + delete widget; QgsLayoutView *view = new QgsLayoutView(); //should use metadata's method - QgsLayoutViewRubberBand *band = QgsApplication::layoutItemRegistry()->createItemRubberBand( 2, view ); + QgsLayoutViewRubberBand *band = registry.createItemRubberBand( 2, view ); QVERIFY( band ); QVERIFY( dynamic_cast< QgsLayoutViewRectangularRubberBand * >( band ) ); QCOMPARE( band->view(), view ); delete band; - //manually register a prototype - QgsLayoutItemRegistryGuiUtils::setItemRubberBandPrototype( 2, new QgsLayoutViewEllipticalRubberBand() ); - band = QgsApplication::layoutItemRegistry()->createItemRubberBand( 2, view ); - QVERIFY( band ); - QVERIFY( dynamic_cast< QgsLayoutViewEllipticalRubberBand * >( band ) ); - QCOMPARE( band->view(), view ); - + //test populate + QgsLayoutItemGuiRegistry reg2; + QVERIFY( reg2.itemTypes().isEmpty() ); + QVERIFY( reg2.populate() ); + QVERIFY( !reg2.itemTypes().isEmpty() ); + QVERIFY( !reg2.populate() ); } void TestQgsLayoutView::rubberBand()