diff --git a/python/core/layout/qgslayoutitemregistry.sip b/python/core/layout/qgslayoutitemregistry.sip index 62ff7ed243a..4ed4c2dee98 100644 --- a/python/core/layout/qgslayoutitemregistry.sip +++ b/python/core/layout/qgslayoutitemregistry.sip @@ -61,6 +61,7 @@ class QgsLayoutItemAbstractMetadata :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). @@ -78,6 +79,7 @@ class QgsLayoutItemAbstractMetadata + class QgsLayoutItemRegistry : QObject { %Docstring @@ -141,6 +143,7 @@ class QgsLayoutItemRegistry : QObject :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 313186e0f9b..87d8f3dd83e 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -42,7 +42,9 @@ %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 %Include raster/qgsrasterrendererwidget.sip %Include symbology-ng/qgssymbolwidgetcontext.sip diff --git a/python/gui/layout/qgslayoutitemregistryguiutils.sip b/python/gui/layout/qgslayoutitemregistryguiutils.sip new file mode 100644 index 00000000000..2387b0e11cb --- /dev/null +++ b/python/gui/layout/qgslayoutitemregistryguiutils.sip @@ -0,0 +1,46 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/layout/qgslayoutitemregistryguiutils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsLayoutItemRegistryGuiUtils +{ +%Docstring + 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. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutitemregistryguiutils.h" +%End + public: + + static void setItemRubberBandPrototype( int type, QgsLayoutViewRubberBand *prototype /Transfer/ ); +%Docstring + Sets a ``prototype`` for the rubber bands for the layout item with specified ``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. +%End + + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/layout/qgslayoutitemregistryguiutils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/layout/qgslayoutviewrubberband.sip b/python/gui/layout/qgslayoutviewrubberband.sip new file mode 100644 index 00000000000..becc3addccb --- /dev/null +++ b/python/gui/layout/qgslayoutviewrubberband.sip @@ -0,0 +1,157 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/layout/qgslayoutviewrubberband.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsLayoutViewRubberBand +{ +%Docstring + QgsLayoutViewRubberBand is an abstract base class for temporary rubber band items + in various shapes, for use within QgsLayoutView widgets. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutviewrubberband.h" +%End + +%ConvertToSubClassCode + if ( dynamic_cast( sipCpp ) ) + sipType = sipType_QgsLayoutViewMouseEvent; + else + sipType = 0; +%End + public: + + QgsLayoutViewRubberBand( QgsLayoutView *view ); +%Docstring + Constructor for QgsLayoutViewRubberBand. +%End + + virtual ~QgsLayoutViewRubberBand(); + + virtual QgsLayoutViewRubberBand *create( QgsLayoutView *view ) const = 0 /Factory/; +%Docstring + Creates a new instance of the QgsLayoutViewRubberBand subclass. + :rtype: QgsLayoutViewRubberBand +%End + + virtual void start( QPointF position, Qt::KeyboardModifiers modifiers ) = 0; +%Docstring + Called when a rubber band should be created at the specified + starting ``position`` (in layout coordinate space). +%End + + virtual void update( QPointF position, Qt::KeyboardModifiers modifiers ) = 0; +%Docstring + Called when a rubber band should be updated to reflect a temporary + ending ``position`` (in layout coordinate space). +%End + + virtual void finish( QPointF position, Qt::KeyboardModifiers modifiers ) = 0; +%Docstring + Called when a rubber band use has finished and the rubber + band is no longer required. +%End + + QgsLayoutView *view() const; +%Docstring + Returns the view associated with the rubber band. +.. seealso:: layout() + :rtype: QgsLayoutView +%End + + QgsLayout *layout() const; +%Docstring + Returns the layout associated with the rubber band. +.. seealso:: view() + :rtype: QgsLayout +%End + + protected: + + QRectF updateRect( QPointF start, QPointF position, bool constrainSquare, bool fromCenter ); +%Docstring + Calculates an updated bounding box rectangle from a original ``start`` position + and new ``position``. If ``constrainSquare`` is true then the bounding box will be + forced to a square shape. If ``fromCenter`` is true then the original ``start`` + position will form the center point of the returned rectangle. + :rtype: QRectF +%End + +}; + + +class QgsLayoutViewRectangularRubberBand : QgsLayoutViewRubberBand +{ +%Docstring + QgsLayoutViewRectangularRubberBand is rectangular rubber band for use within QgsLayoutView widgets. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutviewrubberband.h" +%End + public: + + QgsLayoutViewRectangularRubberBand( QgsLayoutView *view ); +%Docstring + Constructor for QgsLayoutViewRectangularRubberBand. +%End + virtual QgsLayoutViewRectangularRubberBand *create( QgsLayoutView *view ) const /Factory/; + + + ~QgsLayoutViewRectangularRubberBand(); + + virtual void start( QPointF position, Qt::KeyboardModifiers modifiers ); + + virtual void update( QPointF position, Qt::KeyboardModifiers modifiers ); + + virtual void finish( QPointF, Qt::KeyboardModifiers ); + + +}; + +class QgsLayoutViewEllipticalRubberBand : QgsLayoutViewRubberBand +{ +%Docstring + QgsLayoutViewEllipseRubberBand is elliptical rubber band for use within QgsLayoutView widgets. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutviewrubberband.h" +%End + public: + + QgsLayoutViewEllipticalRubberBand( QgsLayoutView *view ); +%Docstring + Constructor for QgsLayoutViewEllipticalRubberBand. +%End + virtual QgsLayoutViewEllipticalRubberBand *create( QgsLayoutView *view ) const /Factory/; + + + ~QgsLayoutViewEllipticalRubberBand(); + + virtual void start( QPointF position, Qt::KeyboardModifiers modifiers ); + + virtual void update( QPointF position, Qt::KeyboardModifiers modifiers ); + + virtual void finish( QPointF, Qt::KeyboardModifiers ); + + +}; +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/layout/qgslayoutviewrubberband.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/layout/qgslayoutviewtooladditem.sip b/python/gui/layout/qgslayoutviewtooladditem.sip index 4289f08acb6..e7d8568a3f3 100644 --- a/python/gui/layout/qgslayoutviewtooladditem.sip +++ b/python/gui/layout/qgslayoutviewtooladditem.sip @@ -8,7 +8,6 @@ - class QgsLayoutViewToolAddItem : QgsLayoutViewTool { %Docstring diff --git a/src/core/layout/qgslayoutitemregistry.cpp b/src/core/layout/qgslayoutitemregistry.cpp index 53cd3305901..347b72aca52 100644 --- a/src/core/layout/qgslayoutitemregistry.cpp +++ b/src/core/layout/qgslayoutitemregistry.cpp @@ -58,6 +58,17 @@ QWidget *QgsLayoutItemRegistry::createItemWidget( int type ) const 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 0f41ea1bb0e..ba055359e4a 100644 --- a/src/core/layout/qgslayoutitemregistry.h +++ b/src/core/layout/qgslayoutitemregistry.h @@ -25,7 +25,9 @@ #include class QgsLayout; +class QgsLayoutView; class QgsLayoutItem; +class QgsLayoutViewRubberBand; /** * \ingroup core @@ -73,6 +75,14 @@ class CORE_EXPORT QgsLayoutItemAbstractMetadata */ 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, @@ -99,6 +109,9 @@ typedef std::function QgsLa //! 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; @@ -152,6 +165,18 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata */ 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; } @@ -165,6 +190,7 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata QIcon mIcon; QgsLayoutItemCreateFunc mCreateFunc = nullptr; QgsLayoutItemWidgetFunc mWidgetFunc = nullptr; + QgsLayoutItemRubberBandFunc mRubberBandFunc = nullptr; QgsLayoutItemPathResolverFunc mPathResolverFunc = nullptr; }; @@ -237,6 +263,12 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject */ 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 @@ -264,6 +296,10 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject QMap mMetadata; + QMap mRubberBandFunctions; + + friend class QgsLayoutItemRegistryGuiUtils; + }; #endif //QGSLAYOUTITEMREGISTRY_H diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 6ec76aaa365..2ab7e2e7eb9 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -158,8 +158,10 @@ SET(QGIS_GUI_SRCS layertree/qgslayertreeview.cpp layertree/qgslayertreeviewdefaultactions.cpp + layout/qgslayoutitemregistryguiutils.cpp layout/qgslayoutview.cpp layout/qgslayoutviewmouseevent.cpp + layout/qgslayoutviewrubberband.cpp layout/qgslayoutviewtool.cpp layout/qgslayoutviewtooladditem.cpp @@ -721,7 +723,9 @@ SET(QGIS_GUI_HDRS layertree/qgslayertreeembeddedconfigwidget.h layertree/qgslayertreeembeddedwidgetregistry.h + layout/qgslayoutitemregistryguiutils.h layout/qgslayoutviewmouseevent.h + layout/qgslayoutviewrubberband.h locator/qgslocatorcontext.h diff --git a/src/gui/layout/qgslayoutitemregistryguiutils.cpp b/src/gui/layout/qgslayoutitemregistryguiutils.cpp new file mode 100644 index 00000000000..a63b2dd75f8 --- /dev/null +++ b/src/gui/layout/qgslayoutitemregistryguiutils.cpp @@ -0,0 +1,27 @@ +/*************************************************************************** + 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 new file mode 100644 index 00000000000..26d899f8cf6 --- /dev/null +++ b/src/gui/layout/qgslayoutitemregistryguiutils.h @@ -0,0 +1,50 @@ +/*************************************************************************** + 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/qgslayoutviewrubberband.cpp b/src/gui/layout/qgslayoutviewrubberband.cpp new file mode 100644 index 00000000000..db5b3bd0646 --- /dev/null +++ b/src/gui/layout/qgslayoutviewrubberband.cpp @@ -0,0 +1,222 @@ +/*************************************************************************** + qgslayoutviewrubberband.cpp + --------------------------- + 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 "qgslayoutviewrubberband.h" +#include "qgslayout.h" +#include "qgslayoutview.h" +#include +#include + +QgsLayoutViewRubberBand::QgsLayoutViewRubberBand( QgsLayoutView *view ) + : mView( view ) +{ + +} + +QgsLayoutView *QgsLayoutViewRubberBand::view() const +{ + return mView; +} + +QgsLayout *QgsLayoutViewRubberBand::layout() const +{ + return mView->currentLayout(); +} + +QRectF QgsLayoutViewRubberBand::updateRect( QPointF start, QPointF position, bool constrainSquare, bool fromCenter ) +{ + double x = 0; + double y = 0; + double width = 0; + double height = 0; + + double dx = position.x() - start.x(); + double dy = position.y() - start.y(); + + if ( constrainSquare ) + { + if ( fabs( dx ) > fabs( dy ) ) + { + width = fabs( dx ); + height = width; + } + else + { + height = fabs( dy ); + width = height; + } + + x = start.x() - ( ( dx < 0 ) ? width : 0 ); + y = start.y() - ( ( dy < 0 ) ? height : 0 ); + } + else + { + //not constraining + if ( dx < 0 ) + { + x = position.x(); + width = -dx; + } + else + { + x = start.x(); + width = dx; + } + + if ( dy < 0 ) + { + y = position.y(); + height = -dy; + } + else + { + y = start.y(); + height = dy; + } + } + + if ( fromCenter ) + { + x = start.x() - width; + y = start.y() - height; + width *= 2.0; + height *= 2.0; + } + + return QRectF( x, y, width, height ); +} + + +QgsLayoutViewRectangularRubberBand::QgsLayoutViewRectangularRubberBand( QgsLayoutView *view ) + : QgsLayoutViewRubberBand( view ) +{ +} + +QgsLayoutViewRectangularRubberBand *QgsLayoutViewRectangularRubberBand::create( QgsLayoutView *view ) const +{ + return new QgsLayoutViewRectangularRubberBand( view ); +} + +QgsLayoutViewRectangularRubberBand::~QgsLayoutViewRectangularRubberBand() +{ + if ( mRubberBandItem ) + { + layout()->removeItem( mRubberBandItem ); + delete mRubberBandItem; + } +} + +void QgsLayoutViewRectangularRubberBand::start( QPointF position, Qt::KeyboardModifiers ) +{ + QTransform t; + mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 ); + mRubberBandItem->setBrush( Qt::NoBrush ); + mRubberBandItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) ); + mRubberBandStartPos = position; + t.translate( position.x(), position.y() ); + mRubberBandItem->setTransform( t ); + mRubberBandItem->setZValue( QgsLayout::ZMapTool ); + layout()->addItem( mRubberBandItem ); + layout()->update(); +} + +void QgsLayoutViewRectangularRubberBand::update( QPointF position, Qt::KeyboardModifiers modifiers ) +{ + if ( !mRubberBandItem ) + { + return; + } + + bool constrainSquare = modifiers & Qt::ShiftModifier; + bool fromCenter = modifiers & Qt::AltModifier; + + QRectF newRect = updateRect( mRubberBandStartPos, position, constrainSquare, fromCenter ); + mRubberBandItem->setRect( 0, 0, newRect.width(), newRect.height() ); + QTransform t; + t.translate( newRect.x(), newRect.y() ); + mRubberBandItem->setTransform( t ); +} + +void QgsLayoutViewRectangularRubberBand::finish( QPointF, Qt::KeyboardModifiers ) +{ + if ( mRubberBandItem ) + { + layout()->removeItem( mRubberBandItem ); + delete mRubberBandItem; + mRubberBandItem = nullptr; + } +} + +QgsLayoutViewEllipticalRubberBand::QgsLayoutViewEllipticalRubberBand( QgsLayoutView *view ) + : QgsLayoutViewRubberBand( view ) +{ + +} + +QgsLayoutViewEllipticalRubberBand *QgsLayoutViewEllipticalRubberBand::create( QgsLayoutView *view ) const +{ + return new QgsLayoutViewEllipticalRubberBand( view ); +} + +QgsLayoutViewEllipticalRubberBand::~QgsLayoutViewEllipticalRubberBand() +{ + if ( mRubberBandItem ) + { + layout()->removeItem( mRubberBandItem ); + delete mRubberBandItem; + } +} + +void QgsLayoutViewEllipticalRubberBand::start( QPointF position, Qt::KeyboardModifiers ) +{ + QTransform t; + mRubberBandItem = new QGraphicsEllipseItem( 0, 0, 0, 0 ); + mRubberBandItem->setBrush( Qt::NoBrush ); + mRubberBandItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) ); + mRubberBandStartPos = position; + t.translate( position.x(), position.y() ); + mRubberBandItem->setTransform( t ); + mRubberBandItem->setZValue( QgsLayout::ZMapTool ); + layout()->addItem( mRubberBandItem ); + layout()->update(); +} + +void QgsLayoutViewEllipticalRubberBand::update( QPointF position, Qt::KeyboardModifiers modifiers ) +{ + if ( !mRubberBandItem ) + { + return; + } + + bool constrainSquare = modifiers & Qt::ShiftModifier; + bool fromCenter = modifiers & Qt::AltModifier; + + QRectF newRect = updateRect( mRubberBandStartPos, position, constrainSquare, fromCenter ); + mRubberBandItem->setRect( 0, 0, newRect.width(), newRect.height() ); + QTransform t; + t.translate( newRect.x(), newRect.y() ); + mRubberBandItem->setTransform( t ); +} + +void QgsLayoutViewEllipticalRubberBand::finish( QPointF, Qt::KeyboardModifiers ) +{ + if ( mRubberBandItem ) + { + layout()->removeItem( mRubberBandItem ); + delete mRubberBandItem; + mRubberBandItem = nullptr; + } +} diff --git a/src/gui/layout/qgslayoutviewrubberband.h b/src/gui/layout/qgslayoutviewrubberband.h new file mode 100644 index 00000000000..f1a924f20c0 --- /dev/null +++ b/src/gui/layout/qgslayoutviewrubberband.h @@ -0,0 +1,169 @@ +/*************************************************************************** + qgslayoutviewrubberband.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 QGSLAYOUTVIEWRUBBERBAND_H +#define QGSLAYOUTVIEWRUBBERBAND_H + +#include + +#include "qgis_gui.h" +#include "qgis_sip.h" + +class QgsLayoutView; +class QGraphicsRectItem; +class QGraphicsEllipseItem; +class QgsLayout; + +/** + * \ingroup gui + * QgsLayoutViewRubberBand is an abstract base class for temporary rubber band items + * in various shapes, for use within QgsLayoutView widgets. + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsLayoutViewRubberBand +{ + +#ifdef SIP_RUN + SIP_CONVERT_TO_SUBCLASS_CODE + if ( dynamic_cast( sipCpp ) ) + sipType = sipType_QgsLayoutViewMouseEvent; + else + sipType = 0; + SIP_END +#endif + + public: + + /** + * Constructor for QgsLayoutViewRubberBand. + */ + QgsLayoutViewRubberBand( QgsLayoutView *view ); + + virtual ~QgsLayoutViewRubberBand() = default; + + /** + * Creates a new instance of the QgsLayoutViewRubberBand subclass. + */ + virtual QgsLayoutViewRubberBand *create( QgsLayoutView *view ) const = 0 SIP_FACTORY; + + /** + * Called when a rubber band should be created at the specified + * starting \a position (in layout coordinate space). + */ + virtual void start( QPointF position, Qt::KeyboardModifiers modifiers ) = 0; + + /** + * Called when a rubber band should be updated to reflect a temporary + * ending \a position (in layout coordinate space). + */ + virtual void update( QPointF position, Qt::KeyboardModifiers modifiers ) = 0; + + /** + * Called when a rubber band use has finished and the rubber + * band is no longer required. + */ + virtual void finish( QPointF position, Qt::KeyboardModifiers modifiers ) = 0; + + /** + * Returns the view associated with the rubber band. + * \see layout() + */ + QgsLayoutView *view() const; + + /** + * Returns the layout associated with the rubber band. + * \see view() + */ + QgsLayout *layout() const; + + protected: + + /** + * Calculates an updated bounding box rectangle from a original \a start position + * and new \a position. If \a constrainSquare is true then the bounding box will be + * forced to a square shape. If \a fromCenter is true then the original \a start + * position will form the center point of the returned rectangle. + */ + QRectF updateRect( QPointF start, QPointF position, bool constrainSquare, bool fromCenter ); + + private: + + QgsLayoutView *mView = nullptr; + +}; + + +/** + * \ingroup gui + * QgsLayoutViewRectangularRubberBand is rectangular rubber band for use within QgsLayoutView widgets. + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsLayoutViewRectangularRubberBand : public QgsLayoutViewRubberBand +{ + public: + + /** + * Constructor for QgsLayoutViewRectangularRubberBand. + */ + QgsLayoutViewRectangularRubberBand( QgsLayoutView *view ); + QgsLayoutViewRectangularRubberBand *create( QgsLayoutView *view ) const override SIP_FACTORY; + + ~QgsLayoutViewRectangularRubberBand(); + + void start( QPointF position, Qt::KeyboardModifiers modifiers ) override; + void update( QPointF position, Qt::KeyboardModifiers modifiers ) override; + void finish( QPointF, Qt::KeyboardModifiers ) override; + + private: + + //! Rubber band item + QGraphicsRectItem *mRubberBandItem = nullptr; + + //! Start of rubber band creation + QPointF mRubberBandStartPos; + +}; + +/** + * \ingroup gui + * QgsLayoutViewEllipseRubberBand is elliptical rubber band for use within QgsLayoutView widgets. + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsLayoutViewEllipticalRubberBand : public QgsLayoutViewRubberBand +{ + public: + + /** + * Constructor for QgsLayoutViewEllipticalRubberBand. + */ + QgsLayoutViewEllipticalRubberBand( QgsLayoutView *view ); + QgsLayoutViewEllipticalRubberBand *create( QgsLayoutView *view ) const override SIP_FACTORY; + + ~QgsLayoutViewEllipticalRubberBand(); + + void start( QPointF position, Qt::KeyboardModifiers modifiers ) override; + void update( QPointF position, Qt::KeyboardModifiers modifiers ) override; + void finish( QPointF, Qt::KeyboardModifiers ) override; + + private: + + //! Rubber band item + QGraphicsEllipseItem *mRubberBandItem = nullptr; + + //! Start of rubber band creation + QPointF mRubberBandStartPos; + +}; +#endif // QGSLAYOUTVIEWRUBBERBAND_H diff --git a/src/gui/layout/qgslayoutviewtooladditem.cpp b/src/gui/layout/qgslayoutviewtooladditem.cpp index 77f7c7854be..705f52edafd 100644 --- a/src/gui/layout/qgslayoutviewtooladditem.cpp +++ b/src/gui/layout/qgslayoutviewtooladditem.cpp @@ -21,6 +21,7 @@ #include "qgslayoutitemregistry.h" #include "qgslayoutviewmouseevent.h" #include "qgslogger.h" +#include "qgslayoutviewrubberband.h" #include #include #include @@ -40,22 +41,24 @@ void QgsLayoutViewToolAddItem::setItemType( int itemType ) void QgsLayoutViewToolAddItem::layoutPressEvent( QgsLayoutViewMouseEvent *event ) { - QTransform t; - mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 ); - mRubberBandItem->setBrush( Qt::NoBrush ); - mRubberBandItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) ); - mMousePressStartPos = event->pos(); - mRubberBandStartPos = QPointF( event->layoutPoint().x(), event->layoutPoint().y() ); - t.translate( event->x(), event->y() ); - mRubberBandItem->setTransform( t ); - mRubberBandItem->setZValue( QgsLayout::ZMapTool ); - layout()->addItem( mRubberBandItem ); - layout()->update(); + if ( event->button() != Qt::LeftButton ) + { + return; + } + + mRubberBand.reset( QgsApplication::layoutItemRegistry()->createItemRubberBand( mItemType, view() ) ); + if ( mRubberBand ) + { + mRubberBand->start( event->layoutPoint(), event->modifiers() ); + } } void QgsLayoutViewToolAddItem::layoutMoveEvent( QgsLayoutViewMouseEvent *event ) { - updateRubberBandRect( event->layoutPoint(), event->modifiers() & Qt::ShiftModifier, event->modifiers() & Qt::AltModifier ); + if ( mRubberBand ) + { + mRubberBand->update( event->layoutPoint(), event->modifiers() ); + } } void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *event ) @@ -65,6 +68,11 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even return; } + if ( mRubberBand ) + { + mRubberBand->finish( event->layoutPoint(), event->modifiers() ); + } + // click? or click-and-drag? QPoint mousePressStopPoint = event->pos(); int diffX = mousePressStopPoint.x() - mMousePressStartPos.x(); @@ -74,88 +82,12 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even { clickOnly = true; } + Q_UNUSED( clickOnly ); - if ( mRubberBandItem ) - { - layout()->removeItem( mRubberBandItem ); - delete mRubberBandItem; - mRubberBandItem = nullptr; - } QgsLogger::debug( QStringLiteral( "creating new %1 item " ).arg( QgsApplication::layoutItemRegistry()->itemMetadata( mItemType )->visibleName() ) ); } -void QgsLayoutViewToolAddItem::updateRubberBandRect( QPointF pos, const bool constrainSquare, const bool fromCenter ) -{ - if ( !mRubberBandItem ) - { - return; - } - - double x = 0; - double y = 0; - double width = 0; - double height = 0; - - double dx = pos.x() - mRubberBandStartPos.x(); - double dy = pos.y() - mRubberBandStartPos.y(); - - if ( constrainSquare ) - { - if ( fabs( dx ) > fabs( dy ) ) - { - width = fabs( dx ); - height = width; - } - else - { - height = fabs( dy ); - width = height; - } - - x = mRubberBandStartPos.x() - ( ( dx < 0 ) ? width : 0 ); - y = mRubberBandStartPos.y() - ( ( dy < 0 ) ? height : 0 ); - } - else - { - //not constraining - if ( dx < 0 ) - { - x = pos.x(); - width = -dx; - } - else - { - x = mRubberBandStartPos.x(); - width = dx; - } - - if ( dy < 0 ) - { - y = pos.y(); - height = -dy; - } - else - { - y = mRubberBandStartPos.y(); - height = dy; - } - } - - if ( fromCenter ) - { - x = mRubberBandStartPos.x() - width; - y = mRubberBandStartPos.y() - height; - width *= 2.0; - height *= 2.0; - } - - mRubberBandItem->setRect( 0, 0, width, height ); - QTransform t; - t.translate( x, y ); - mRubberBandItem->setTransform( t ); -} - int QgsLayoutViewToolAddItem::itemType() const { return mItemType; diff --git a/src/gui/layout/qgslayoutviewtooladditem.h b/src/gui/layout/qgslayoutviewtooladditem.h index 4393b2b8f1c..fb5a860ae57 100644 --- a/src/gui/layout/qgslayoutviewtooladditem.h +++ b/src/gui/layout/qgslayoutviewtooladditem.h @@ -19,8 +19,8 @@ #include "qgis.h" #include "qgis_gui.h" #include "qgslayoutviewtool.h" - -class QGraphicsRectItem; +#include "qgslayoutviewrubberband.h" +#include /** * \ingroup gui @@ -57,7 +57,7 @@ class GUI_EXPORT QgsLayoutViewToolAddItem : public QgsLayoutViewTool int mItemType = 0; //! Rubber band item - QGraphicsRectItem *mRubberBandItem = nullptr; + std::unique_ptr< QgsLayoutViewRubberBand > mRubberBand; //! Start position for mouse press QPoint mMousePressStartPos; @@ -65,9 +65,6 @@ class GUI_EXPORT QgsLayoutViewToolAddItem : public QgsLayoutViewTool //! Start of rubber band creation QPointF mRubberBandStartPos; - //! Redraws the rectangular rubber band - void updateRubberBandRect( QPointF pos, const bool constrainSquare = false, const bool fromCenter = false ); - }; #endif // QGSLAYOUTVIEWTOOLADDITEM_H