Skip to content

Commit

Permalink
Rework API to use composition pattern instead of direct inheritance
Browse files Browse the repository at this point in the history
If we require all annotation item map tools to inherit from
QgsMapToolAdvancedDigitizing, then we lose the flexibility to
subclasss other map tools for annotation item creation (e.g.
QgsMapToolCapture)
  • Loading branch information
nyalldawson committed Sep 8, 2021
1 parent eb58e74 commit ef25d91
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 56 deletions.
Expand Up @@ -70,7 +70,7 @@ Returns an icon representing creation of the annotation item type.
Creates a configuration widget for an ``item`` of this type. Can return ``None`` if no configuration GUI is required.
%End

virtual QgsCreateAnnotationItemMapTool *createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) /TransferBack/;
virtual QgsCreateAnnotationItemMapToolInterface *createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) /TransferBack/;
%Docstring
Creates a map tool for a creating a new item of this type.

Expand Down
Expand Up @@ -8,13 +8,16 @@



class QgsCreateAnnotationItemMapTool: QgsMapToolAdvancedDigitizing
class QgsCreateAnnotationItemMapToolHandler : QObject
{
%Docstring(signature="appended")

A base class for map tools which create annotation items.
A handler object for map tools which create annotation items.

Clients should connect to the map tool's :py:func:`~itemCreated` signal, and call the
This object is designed to be used by map tools which implement the
:py:class:`QgsCreateAnnotationItemMapToolInterface`, following the composition pattern.

Clients should connect to the handler's :py:func:`~itemCreated` signal, and call the
:py:func:`~takeCreatedItem` implementation to take ownership of the newly created item
whenever this signal is emitted.

Expand All @@ -26,17 +29,31 @@ whenever this signal is emitted.
%End
public:

QgsCreateAnnotationItemMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget );
QgsCreateAnnotationItemMapToolHandler( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, QObject *parent = 0 );
%Docstring
Constructor for QgsCreateAnnotationItemMapTool.
Constructor for QgsCreateAnnotationItemMapToolHandler, with the specified ``parent`` object.
%End

virtual QgsAnnotationItem *takeCreatedItem() = 0 /TransferBack/;
~QgsCreateAnnotationItemMapToolHandler();

QgsAnnotationItem *takeCreatedItem() /TransferBack/;
%Docstring
Takes the newly created item from the tool, transferring ownership to the caller.
%End

QgsAnnotationLayer *targetLayer();
%Docstring
Returns the target layer for newly created items.
%End

Subclasses must implement this method, and ensure that they emit the :py:func:`~QgsCreateAnnotationItemMapTool.itemCreated` signal whenever
they have a created item ready for clients to take.
void pushCreatedItem( QgsAnnotationItem *item /Transfer/ );
%Docstring
Pushes a created ``item`` to the handler.

Ownership of ``item`` is transferred to the handler.

Calling this method causes the object to emit the :py:func:`~QgsCreateAnnotationItemMapToolHandler.itemCreated` signal, and queue the item
ready for collection via a call to :py:func:`~QgsCreateAnnotationItemMapToolHandler.takeCreatedItem`.
%End

signals:
Expand All @@ -45,16 +62,40 @@ they have a created item ready for clients to take.
%Docstring
Emitted by the tool when a new annotation item has been created.

Clients should connect to this signal and call :py:func:`~QgsCreateAnnotationItemMapTool.takeCreatedItem` to take the newly created item from the map tool.
Clients should connect to this signal and call :py:func:`~QgsCreateAnnotationItemMapToolHandler.takeCreatedItem` to take the newly created item from the map tool.
%End

protected:
};

class QgsCreateAnnotationItemMapToolInterface
{
%Docstring(signature="appended")

QgsAnnotationLayer *targetLayer();
An interface for map tools which create annotation items.

Clients should connect to the map tool's :py:func:`~QgsCreateAnnotationItemMapToolHandler.itemCreated` signal, and call the
:py:func:`~QgsCreateAnnotationItemMapToolHandler.takeCreatedItem` implementation to take ownership of the newly created item
whenever this signal is emitted.

.. versionadded:: 3.22
%End

%TypeHeaderCode
#include "qgscreateannotationitemmaptool.h"
%End
public:

virtual ~QgsCreateAnnotationItemMapToolInterface();

virtual QgsCreateAnnotationItemMapToolHandler *handler() = 0;
%Docstring
Returns the target layer for newly created items.
Returns the handler object for the map tool.
%End

virtual QgsMapTool *mapTool() = 0;
%Docstring
Returns a reference to the associated map tool.
%End
};

/************************************************************************
Expand Down
10 changes: 5 additions & 5 deletions src/app/qgisapp.cpp
Expand Up @@ -804,12 +804,12 @@ void QgisApp::annotationItemTypeAdded( int id )

connect( action, &QAction::triggered, this, [this, id]()
{
QgsCreateAnnotationItemMapTool *tool = QgsGui::annotationItemGuiRegistry()->itemMetadata( id )->createMapTool( mMapCanvas, mAdvancedDigitizingDockWidget );
mMapCanvas->setMapTool( tool );
connect( tool, &QgsMapTool::deactivated, tool, &QObject::deleteLater );
connect( tool, &QgsCreateAnnotationItemMapTool::itemCreated, this, [ = ]
QgsCreateAnnotationItemMapToolInterface *tool = QgsGui::annotationItemGuiRegistry()->itemMetadata( id )->createMapTool( mMapCanvas, mAdvancedDigitizingDockWidget );
mMapCanvas->setMapTool( tool->mapTool() );
connect( tool->mapTool(), &QgsMapTool::deactivated, tool->mapTool(), &QObject::deleteLater );
connect( tool->handler(), &QgsCreateAnnotationItemMapToolHandler::itemCreated, this, [ = ]
{
QgsAnnotationItem *item = tool->takeCreatedItem();
QgsAnnotationItem *item = tool->handler()->takeCreatedItem();
QgsAnnotationLayer *targetLayer = qobject_cast< QgsAnnotationLayer * >( activeLayer() );
if ( !targetLayer )
targetLayer = QgsProject::instance()->mainAnnotationLayer();
Expand Down
10 changes: 5 additions & 5 deletions src/gui/annotations/qgsannotationitemguiregistry.cpp
Expand Up @@ -34,7 +34,7 @@ QgsAnnotationItemBaseWidget *QgsAnnotationItemAbstractGuiMetadata::createItemWid
return nullptr;
}

QgsCreateAnnotationItemMapTool *QgsAnnotationItemAbstractGuiMetadata::createMapTool( QgsMapCanvas *, QgsAdvancedDigitizingDockWidget * )
QgsCreateAnnotationItemMapToolInterface *QgsAnnotationItemAbstractGuiMetadata::createMapTool( QgsMapCanvas *, QgsAdvancedDigitizingDockWidget * )
{
return nullptr;
}
Expand Down Expand Up @@ -74,7 +74,7 @@ void QgsAnnotationItemGuiMetadata::newItemAddedToLayer( QgsAnnotationItem *item,
mAddedToLayerFunc( item, layer );
}

QgsCreateAnnotationItemMapTool *QgsAnnotationItemGuiMetadata::createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget )
QgsCreateAnnotationItemMapToolInterface *QgsAnnotationItemGuiMetadata::createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget )
{
return mCreateMapToolFunc ? mCreateMapToolFunc( canvas, cadDockWidget ) : nullptr;
}
Expand Down Expand Up @@ -180,7 +180,7 @@ void QgsAnnotationItemGuiRegistry::addDefaultItems()
{
addAnnotationItemGuiMetadata( new QgsAnnotationItemGuiMetadata( QStringLiteral( "polygon" ),
QObject::tr( "Polygon" ),
QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddImage.svg" ) ),
QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddPolygon.svg" ) ),
[ = ]( QgsAnnotationItem * item )->QgsAnnotationItemBaseWidget *
{
QgsAnnotationPolygonItemWidget *widget = new QgsAnnotationPolygonItemWidget( nullptr );
Expand All @@ -190,7 +190,7 @@ void QgsAnnotationItemGuiRegistry::addDefaultItems()

addAnnotationItemGuiMetadata( new QgsAnnotationItemGuiMetadata( QStringLiteral( "linestring" ),
QObject::tr( "Line" ),
QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddImage.svg" ) ),
QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddPolyline.svg" ) ),
[ = ]( QgsAnnotationItem * item )->QgsAnnotationItemBaseWidget *
{
QgsAnnotationLineItemWidget *widget = new QgsAnnotationLineItemWidget( nullptr );
Expand All @@ -217,7 +217,7 @@ void QgsAnnotationItemGuiRegistry::addDefaultItems()
widget->setItem( item );
return widget;
}, QString(), Qgis::AnnotationItemGuiFlags(), nullptr,
[ = ]( QgsMapCanvas * canvas, QgsAdvancedDigitizingDockWidget * cadDockWidget )->QgsCreateAnnotationItemMapTool *
[ = ]( QgsMapCanvas * canvas, QgsAdvancedDigitizingDockWidget * cadDockWidget )->QgsCreateAnnotationItemMapToolInterface *
{
return new QgsCreatePointTextItemMapTool( canvas, cadDockWidget );
} ) );
Expand Down
8 changes: 4 additions & 4 deletions src/gui/annotations/qgsannotationitemguiregistry.h
Expand Up @@ -28,7 +28,7 @@
class QgsAnnotationLayer;
class QgsAnnotationItem;
class QgsAnnotationItemBaseWidget;
class QgsCreateAnnotationItemMapTool;
class QgsCreateAnnotationItemMapToolInterface;
class QgsMapCanvas;
class QgsAdvancedDigitizingDockWidget;

Expand Down Expand Up @@ -112,7 +112,7 @@ class GUI_EXPORT QgsAnnotationItemAbstractGuiMetadata
*
* May return NULLPTR if no map tool is available for creating the item.
*/
virtual QgsCreateAnnotationItemMapTool *createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) SIP_TRANSFERBACK;
virtual QgsCreateAnnotationItemMapToolInterface *createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) SIP_TRANSFERBACK;

/**
* Creates an instance of the corresponding item type.
Expand Down Expand Up @@ -140,7 +140,7 @@ class GUI_EXPORT QgsAnnotationItemAbstractGuiMetadata
typedef std::function<QgsAnnotationItemBaseWidget *( QgsAnnotationItem * )> QgsAnnotationItemWidgetFunc SIP_SKIP;

//! Create annotation map tool creation function
typedef std::function<QgsCreateAnnotationItemMapTool *( QgsMapCanvas *, QgsAdvancedDigitizingDockWidget * )> QgsCreateAnnotationItemMapToolFunc SIP_SKIP;
typedef std::function<QgsCreateAnnotationItemMapToolInterface *( QgsMapCanvas *, QgsAdvancedDigitizingDockWidget * )> QgsCreateAnnotationItemMapToolFunc SIP_SKIP;

//! Annotation item added to layer callback
typedef std::function<void ( QgsAnnotationItem *, QgsAnnotationLayer *layer )> QgsAnnotationItemAddedToLayerFunc SIP_SKIP;
Expand Down Expand Up @@ -232,7 +232,7 @@ class GUI_EXPORT QgsAnnotationItemGuiMetadata : public QgsAnnotationItemAbstract

QgsAnnotationItem *createItem() override;
void newItemAddedToLayer( QgsAnnotationItem *item, QgsAnnotationLayer *layer ) override;
QgsCreateAnnotationItemMapTool *createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) override;
QgsCreateAnnotationItemMapToolInterface *createMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) override;

protected:
QIcon mIcon;
Expand Down
24 changes: 20 additions & 4 deletions src/gui/annotations/qgscreateannotationitemmaptool.cpp
Expand Up @@ -16,17 +16,33 @@
#include "qgscreateannotationitemmaptool.h"
#include "qgsmapcanvas.h"
#include "qgsannotationlayer.h"
#include "qgsannotationitem.h"

QgsCreateAnnotationItemMapTool::QgsCreateAnnotationItemMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget )
: QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
QgsCreateAnnotationItemMapToolHandler::QgsCreateAnnotationItemMapToolHandler( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, QObject *parent )
: QObject( parent )
, mMapCanvas( canvas )
, mCadDockWidget( cadDockWidget )
{

}

QgsAnnotationLayer *QgsCreateAnnotationItemMapTool::targetLayer()
QgsAnnotationItem *QgsCreateAnnotationItemMapToolHandler::takeCreatedItem()
{
if ( QgsAnnotationLayer *res = qobject_cast< QgsAnnotationLayer * >( canvas()->currentLayer() ) )
return mCreatedItem.release();
}

QgsCreateAnnotationItemMapToolHandler::~QgsCreateAnnotationItemMapToolHandler() = default;

QgsAnnotationLayer *QgsCreateAnnotationItemMapToolHandler::targetLayer()
{
if ( QgsAnnotationLayer *res = qobject_cast< QgsAnnotationLayer * >( mMapCanvas->currentLayer() ) )
return res;
else
return QgsProject::instance()->mainAnnotationLayer();
}

void QgsCreateAnnotationItemMapToolHandler::pushCreatedItem( QgsAnnotationItem *item )
{
mCreatedItem.reset( item );
emit itemCreated();
}
69 changes: 57 additions & 12 deletions src/gui/annotations/qgscreateannotationitemmaptool.h
Expand Up @@ -23,35 +23,52 @@ class QgsAnnotationItem;
class QgsAnnotationLayer;

/**
* \class QgsCreateAnnotationItemMapTool
* \class QgsCreateAnnotationItemMapToolHandler
* \ingroup gui
*
* \brief A base class for map tools which create annotation items.
* \brief A handler object for map tools which create annotation items.
*
* Clients should connect to the map tool's itemCreated() signal, and call the
* This object is designed to be used by map tools which implement the
* QgsCreateAnnotationItemMapToolInterface, following the composition pattern.
*
* Clients should connect to the handler's itemCreated() signal, and call the
* takeCreatedItem() implementation to take ownership of the newly created item
* whenever this signal is emitted.
*
* \since QGIS 3.22
*/
class GUI_EXPORT QgsCreateAnnotationItemMapTool: public QgsMapToolAdvancedDigitizing
class GUI_EXPORT QgsCreateAnnotationItemMapToolHandler : public QObject
{
Q_OBJECT

public:

/**
* Constructor for QgsCreateAnnotationItemMapTool.
* Constructor for QgsCreateAnnotationItemMapToolHandler, with the specified \a parent object.
*/
QgsCreateAnnotationItemMapTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget );
QgsCreateAnnotationItemMapToolHandler( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, QObject *parent = nullptr );

~QgsCreateAnnotationItemMapToolHandler() override;

/**
* Takes the newly created item from the tool, transferring ownership to the caller.
*/
QgsAnnotationItem *takeCreatedItem() SIP_TRANSFERBACK;

/**
* Returns the target layer for newly created items.
*/
QgsAnnotationLayer *targetLayer();

/**
* Pushes a created \a item to the handler.
*
* Subclasses must implement this method, and ensure that they emit the itemCreated() signal whenever
* they have a created item ready for clients to take.
* Ownership of \a item is transferred to the handler.
*
* Calling this method causes the object to emit the itemCreated() signal, and queue the item
* ready for collection via a call to takeCreatedItem().
*/
virtual QgsAnnotationItem *takeCreatedItem() = 0 SIP_TRANSFERBACK;
void pushCreatedItem( QgsAnnotationItem *item SIP_TRANSFER );

signals:

Expand All @@ -62,13 +79,41 @@ class GUI_EXPORT QgsCreateAnnotationItemMapTool: public QgsMapToolAdvancedDigiti
*/
void itemCreated();

protected:
private:

QgsMapCanvas *mMapCanvas = nullptr;
QgsAdvancedDigitizingDockWidget *mCadDockWidget = nullptr;
std::unique_ptr< QgsAnnotationItem > mCreatedItem;

};

/**
* \class QgsCreateAnnotationItemMapToolInterface
* \ingroup gui
*
* \brief An interface for map tools which create annotation items.
*
* Clients should connect to the map tool's itemCreated() signal, and call the
* takeCreatedItem() implementation to take ownership of the newly created item
* whenever this signal is emitted.
*
* \since QGIS 3.22
*/
class GUI_EXPORT QgsCreateAnnotationItemMapToolInterface
{
public:

virtual ~QgsCreateAnnotationItemMapToolInterface() = default;

/**
* Returns the target layer for newly created items.
* Returns the handler object for the map tool.
*/
QgsAnnotationLayer *targetLayer();
virtual QgsCreateAnnotationItemMapToolHandler *handler() = 0;

/**
* Returns a reference to the associated map tool.
*/
virtual QgsMapTool *mapTool() = 0;
};

#endif // QGSCREATEANNOTATIONITEMMAPTOOL_H

0 comments on commit ef25d91

Please sign in to comment.