Skip to content

Commit

Permalink
Add framework for retrieving the temporary results of a in-progress
Browse files Browse the repository at this point in the history
edit operation on an annotation item

This is used for the item to return a representative geometry
of what the item would look like if the operation were to be
applied. It's used to generate a rubber band showing a preview
of the modification during interactive editing operations on
an annotation item.
  • Loading branch information
nyalldawson committed Sep 10, 2021
1 parent 1fe8743 commit 1537d39
Show file tree
Hide file tree
Showing 22 changed files with 171 additions and 1 deletion.
Expand Up @@ -129,6 +129,13 @@ Applies an edit ``operation`` to the item.

Returns ``True`` if the operation was successfully applied.

.. versionadded:: 3.22
%End

virtual QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) /Factory/;
%Docstring
Retrieves the results of a transient (in progress) edit ``operation`` on the item.

.. versionadded:: 3.22
%End

Expand Down
Expand Up @@ -79,6 +79,37 @@ Returns the node position after the move occurred (in layer coordinates).

};

class QgsAnnotationItemEditOperationTransientResults
{
%Docstring(signature="appended")
Encapsulates the transient results of an in-progress annotation edit operation.

.. versionadded:: 3.22
%End

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

QgsAnnotationItemEditOperationTransientResults( const QgsGeometry &representativeGeometry );
%Docstring
Constructor for QgsAnnotationItemEditOperationTransientResults.

The ``representativeGeometry`` parameter specifies a geometry (in layer CRS) which represents
the shape of the item if the operation were to be applied. It is used for creating a graphical
representation of the operation during interactive edits.
%End

QgsGeometry representativeGeometry() const;
%Docstring
Returns the geometry (in layer CRS) which represents the shape of the item if the operation were to be applied.

This is used for creating a graphical representation of the operation during interactive edits.
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
Expand Up @@ -43,6 +43,8 @@ Constructor for QgsAnnotationLineItem, with the specified ``linestring``.

virtual bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation );

virtual QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) /Factory/;


static QgsAnnotationLineItem *create() /Factory/;
%Docstring
Expand Down
Expand Up @@ -41,6 +41,8 @@ Constructor for QgsAnnotationMarkerItem, at the specified ``point``.

virtual bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation );

virtual QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) /Factory/;


static QgsAnnotationMarkerItem *create() /Factory/;
%Docstring
Expand Down
Expand Up @@ -57,6 +57,8 @@ Creates a new text at point annotation item.

virtual bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation );

virtual QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) /Factory/;


QgsPointXY point() const;
%Docstring
Expand Down
Expand Up @@ -43,6 +43,8 @@ Constructor for QgsAnnotationPolygonItem, with the specified ``polygon`` geometr

virtual bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation );

virtual QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) /Factory/;


static QgsAnnotationPolygonItem *create() /Factory/;
%Docstring
Expand Down
5 changes: 5 additions & 0 deletions src/core/annotations/qgsannotationitem.cpp
Expand Up @@ -28,6 +28,11 @@ bool QgsAnnotationItem::applyEdit( QgsAbstractAnnotationItemEditOperation * )
return false;
}

QgsAnnotationItemEditOperationTransientResults *QgsAnnotationItem::transientEditResults( QgsAbstractAnnotationItemEditOperation * )
{
return nullptr;
}

QList<QgsAnnotationItemNode> QgsAnnotationItem::nodes() const
{
return {};
Expand Down
8 changes: 8 additions & 0 deletions src/core/annotations/qgsannotationitem.h
Expand Up @@ -30,6 +30,7 @@ class QgsLineSymbol;
class QgsFillSymbol;
class QgsAnnotationItemNode;
class QgsAbstractAnnotationItemEditOperation;
class QgsAnnotationItemEditOperationTransientResults;

/**
* \ingroup core
Expand Down Expand Up @@ -158,6 +159,13 @@ class CORE_EXPORT QgsAnnotationItem
*/
virtual bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation );

/**
* Retrieves the results of a transient (in progress) edit \a operation on the item.
*
* \since QGIS 3.22
*/
virtual QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) SIP_FACTORY;

/**
* Returns the item's z index, which controls the order in which annotation items
* are rendered in the layer.
Expand Down
33 changes: 33 additions & 0 deletions src/core/annotations/qgsannotationitemeditoperation.h
Expand Up @@ -93,4 +93,37 @@ class CORE_EXPORT QgsAnnotationItemEditOperationMoveNode : public QgsAbstractAnn

};

/**
* \ingroup core
* \brief Encapsulates the transient results of an in-progress annotation edit operation.
* \since QGIS 3.22
*/
class CORE_EXPORT QgsAnnotationItemEditOperationTransientResults
{
public:

/**
* Constructor for QgsAnnotationItemEditOperationTransientResults.
*
* The \a representativeGeometry parameter specifies a geometry (in layer CRS) which represents
* the shape of the item if the operation were to be applied. It is used for creating a graphical
* representation of the operation during interactive edits.
*/
QgsAnnotationItemEditOperationTransientResults( const QgsGeometry &representativeGeometry )
: mRepresentativeGeometry( representativeGeometry )
{}

/**
* Returns the geometry (in layer CRS) which represents the shape of the item if the operation were to be applied.
*
* This is used for creating a graphical representation of the operation during interactive edits.
*/
QgsGeometry representativeGeometry() const { return mRepresentativeGeometry; }

private:

QgsGeometry mRepresentativeGeometry;

};

#endif // QGSANNOTATIONITEMEDITOPERATION_H
13 changes: 13 additions & 0 deletions src/core/annotations/qgsannotationlineitem.cpp
Expand Up @@ -115,6 +115,19 @@ bool QgsAnnotationLineItem::applyEdit( QgsAbstractAnnotationItemEditOperation *o
}
}

QgsAnnotationItemEditOperationTransientResults *QgsAnnotationLineItem::transientEditResults( QgsAbstractAnnotationItemEditOperation *operation )
{
if ( QgsAnnotationItemEditOperationMoveNode *moveOperation = dynamic_cast< QgsAnnotationItemEditOperationMoveNode * >( operation ) )
{
std::unique_ptr< QgsCurve > modifiedCurve( mCurve->clone() );
if ( modifiedCurve->moveVertex( moveOperation->nodeId(), QgsPoint( moveOperation->after() ) ) )
{
return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry( std::move( modifiedCurve ) ) );
}
}
return nullptr;
}

QgsAnnotationLineItem *QgsAnnotationLineItem::create()
{
return new QgsAnnotationLineItem( new QgsLineString() );
Expand Down
1 change: 1 addition & 0 deletions src/core/annotations/qgsannotationlineitem.h
Expand Up @@ -47,6 +47,7 @@ class CORE_EXPORT QgsAnnotationLineItem : public QgsAnnotationItem
QgsGeometry rubberBandGeometry() const override;
bool transform( const QTransform &transform ) override;
bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation ) override;
QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) override SIP_FACTORY;

/**
* Creates a new linestring annotation item.
Expand Down
9 changes: 9 additions & 0 deletions src/core/annotations/qgsannotationmarkeritem.cpp
Expand Up @@ -93,6 +93,15 @@ bool QgsAnnotationMarkerItem::applyEdit( QgsAbstractAnnotationItemEditOperation
}
}

QgsAnnotationItemEditOperationTransientResults *QgsAnnotationMarkerItem::transientEditResults( QgsAbstractAnnotationItemEditOperation *operation )
{
if ( QgsAnnotationItemEditOperationMoveNode *moveOperation = dynamic_cast< QgsAnnotationItemEditOperationMoveNode * >( operation ) )
{
return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry::fromPointXY( moveOperation->after() ) );
}
return nullptr;
}

QgsAnnotationMarkerItem *QgsAnnotationMarkerItem::create()
{
return new QgsAnnotationMarkerItem( QgsPoint() );
Expand Down
1 change: 1 addition & 0 deletions src/core/annotations/qgsannotationmarkeritem.h
Expand Up @@ -45,6 +45,7 @@ class CORE_EXPORT QgsAnnotationMarkerItem : public QgsAnnotationItem
Qgis::AnnotationItemFlags flags() const override;
QList< QgsAnnotationItemNode > nodes() const override;
bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation ) override;
QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) override SIP_FACTORY;

/**
* Creates a new marker annotation item.
Expand Down
9 changes: 9 additions & 0 deletions src/core/annotations/qgsannotationpointtextitem.cpp
Expand Up @@ -160,6 +160,15 @@ bool QgsAnnotationPointTextItem::applyEdit( QgsAbstractAnnotationItemEditOperati
}
}

QgsAnnotationItemEditOperationTransientResults *QgsAnnotationPointTextItem::transientEditResults( QgsAbstractAnnotationItemEditOperation *operation )
{
if ( QgsAnnotationItemEditOperationMoveNode *moveOperation = dynamic_cast< QgsAnnotationItemEditOperationMoveNode * >( operation ) )
{
return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry::fromPointXY( moveOperation->after() ) );
}
return nullptr;
}

QgsTextFormat QgsAnnotationPointTextItem::format() const
{
return mTextFormat;
Expand Down
1 change: 1 addition & 0 deletions src/core/annotations/qgsannotationpointtextitem.h
Expand Up @@ -57,6 +57,7 @@ class CORE_EXPORT QgsAnnotationPointTextItem : public QgsAnnotationItem
QList< QgsAnnotationItemNode > nodes() const override;
bool transform( const QTransform &transform ) override;
bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation ) override;
QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) override SIP_FACTORY;

/**
* Returns the point location of the text.
Expand Down
13 changes: 13 additions & 0 deletions src/core/annotations/qgsannotationpolygonitem.cpp
Expand Up @@ -145,6 +145,19 @@ bool QgsAnnotationPolygonItem::applyEdit( QgsAbstractAnnotationItemEditOperation
}
}

QgsAnnotationItemEditOperationTransientResults *QgsAnnotationPolygonItem::transientEditResults( QgsAbstractAnnotationItemEditOperation *operation )
{
if ( QgsAnnotationItemEditOperationMoveNode *moveOperation = dynamic_cast< QgsAnnotationItemEditOperationMoveNode * >( operation ) )
{
std::unique_ptr< QgsCurvePolygon > modifiedPolygon( mPolygon->clone() );
if ( modifiedPolygon->moveVertex( moveOperation->nodeId(), QgsPoint( moveOperation->after() ) ) )
{
return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry( std::move( modifiedPolygon ) ) );
}
}
return nullptr;
}

QgsAnnotationPolygonItem *QgsAnnotationPolygonItem::create()
{
return new QgsAnnotationPolygonItem( new QgsPolygon() );
Expand Down
1 change: 1 addition & 0 deletions src/core/annotations/qgsannotationpolygonitem.h
Expand Up @@ -46,6 +46,7 @@ class CORE_EXPORT QgsAnnotationPolygonItem : public QgsAnnotationItem
QgsGeometry rubberBandGeometry() const override;
bool transform( const QTransform &transform ) override;
bool applyEdit( QgsAbstractAnnotationItemEditOperation *operation ) override;
QgsAnnotationItemEditOperationTransientResults *transientEditResults( QgsAbstractAnnotationItemEditOperation *operation ) override SIP_FACTORY;

/**
* Creates a new polygon annotation item.
Expand Down
1 change: 1 addition & 0 deletions src/gui/annotations/qgsmaptoolmodifyannotation.cpp
Expand Up @@ -25,6 +25,7 @@
#include "qgsrenderedannotationitemdetails.h"
#include "qgsannotationitem.h"
#include "qgsannotationitemnode.h"
#include "qgsannotationitemeditoperation.h"
#include "RTree.h"
#include <QTransform>
#include <QWindow>
Expand Down
7 changes: 7 additions & 0 deletions tests/src/python/test_qgsannotationlineitem.py
Expand Up @@ -101,6 +101,13 @@ def test_apply_move_node_edit(self):
self.assertFalse(item.applyEdit(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 3), QgsPointXY(14, 15), QgsPointXY(19, 20))))
self.assertEqual(item.geometry().asWkt(), 'LineString (12 13, 17 18, 19 20)')

def test_transient_move_operation(self):
item = QgsAnnotationLineItem(QgsLineString([QgsPoint(12, 13), QgsPoint(14, 13), QgsPoint(14, 15)]))
self.assertEqual(item.geometry().asWkt(), 'LineString (12 13, 14 13, 14 15)')

res = item.transientEditResults(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 1), QgsPointXY(14, 13), QgsPointXY(17, 18)))
self.assertEqual(res.representativeGeometry().asWkt(), 'LineString (12 13, 17 18, 14 15)')

def test_rubberbandgeometry(self):
"""
Test creating rubber band geometry
Expand Down
7 changes: 7 additions & 0 deletions tests/src/python/test_qgsannotationmarkeritem.py
Expand Up @@ -95,6 +95,13 @@ def test_apply_move_node_edit(self):
self.assertTrue(item.applyEdit(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 1), QgsPointXY(14, 13), QgsPointXY(17, 18))))
self.assertEqual(item.geometry().asWkt(), 'POINT(17 18)')

def test_transient_move_operation(self):
item = QgsAnnotationMarkerItem(QgsPoint(12, 13))
self.assertEqual(item.geometry().asWkt(), 'POINT(12 13)')

res = item.transientEditResults(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 0), QgsPointXY(12, 13), QgsPointXY(17, 18)))
self.assertEqual(res.representativeGeometry().asWkt(), 'Point (17 18)')

def testReadWriteXml(self):
doc = QDomDocument("testdoc")
elem = doc.createElement('test')
Expand Down
9 changes: 8 additions & 1 deletion tests/src/python/test_qgsannotationpointtextitem.py
Expand Up @@ -103,9 +103,16 @@ def test_apply_move_node_edit(self):
item = QgsAnnotationPointTextItem('my text', QgsPointXY(12, 13))
self.assertEqual(item.point().asWkt(), 'POINT(12 13)')

self.assertTrue(item.applyEdit(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 1), QgsPointXY(14, 13), QgsPointXY(17, 18))))
self.assertTrue(item.applyEdit(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 0), QgsPointXY(14, 13), QgsPointXY(17, 18))))
self.assertEqual(item.point().asWkt(), 'POINT(17 18)')

def test_transient_move_operation(self):
item = QgsAnnotationPointTextItem('my text', QgsPointXY(12, 13))
self.assertEqual(item.point().asWkt(), 'POINT(12 13)')

res = item.transientEditResults(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 0), QgsPointXY(12, 13), QgsPointXY(17, 18)))
self.assertEqual(res.representativeGeometry().asWkt(), 'Point (17 18)')

def testReadWriteXml(self):
doc = QDomDocument("testdoc")
elem = doc.createElement('test')
Expand Down
8 changes: 8 additions & 0 deletions tests/src/python/test_qgsannotationpolygonitem.py
Expand Up @@ -113,6 +113,14 @@ def test_apply_move_node_edit(self):
self.assertFalse(item.applyEdit(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 4), QgsPointXY(14, 15), QgsPointXY(19, 20))))
self.assertEqual(item.geometry().asWkt(), 'Polygon ((19 20, 17 18, 14 15, 19 20))')

def test_transient_move_operation(self):
item = QgsAnnotationPolygonItem(
QgsPolygon(QgsLineString([QgsPoint(12, 13), QgsPoint(14, 13), QgsPoint(14, 15), QgsPoint(12, 13)])))
self.assertEqual(item.geometry().asWkt(), 'Polygon ((12 13, 14 13, 14 15, 12 13))')

res = item.transientEditResults(QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 1), QgsPointXY(14, 13), QgsPointXY(17, 18)))
self.assertEqual(res.representativeGeometry().asWkt(), 'Polygon ((12 13, 17 18, 14 15, 12 13))')

def testReadWriteXml(self):
doc = QDomDocument("testdoc")
elem = doc.createElement('test')
Expand Down

0 comments on commit 1537d39

Please sign in to comment.