Skip to content

Commit

Permalink
Layout designers listen out for new item types in registry and
Browse files Browse the repository at this point in the history
auto create actions for adding new items of the newly registered type

This avoids hard-coding in available item types into layout UI classes,
and allows designers to handle plugin-supplied item types
  • Loading branch information
nyalldawson committed Jul 3, 2017
1 parent 97e8d9c commit c228132
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 9 deletions.
6 changes: 6 additions & 0 deletions python/core/layout/qgslayoutitemregistry.sip
Expand Up @@ -37,6 +37,12 @@ class QgsLayoutItemAbstractMetadata
:rtype: int
%End

virtual QIcon icon() const;
%Docstring
Returns an icon representing the layout item type.
:rtype: QIcon
%End

QString visibleName() const;
%Docstring
Returns a translated, user visible name for the layout item class.
Expand Down
34 changes: 34 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Expand Up @@ -16,6 +16,7 @@
***************************************************************************/

#include "qgslayoutdesignerdialog.h"
#include "qgslayoutitemregistry.h"
#include "qgssettings.h"
#include "qgisapp.h"
#include "qgslogger.h"
Expand All @@ -39,6 +40,7 @@ void QgsAppLayoutDesignerInterface::close()
QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
: QMainWindow( parent, flags )
, mInterface( new QgsAppLayoutDesignerInterface( this ) )
, mToolsActionGroup( new QActionGroup( this ) )
{
QgsSettings settings;
int size = settings.value( QStringLiteral( "IconSize" ), QGIS_ICON_SIZE ).toInt();
Expand All @@ -55,6 +57,16 @@ 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 )
{
itemTypeAdded( typeIt.key(), typeIt.value() );
}
//..and listen out for new item types
connect( QgsApplication::layoutItemRegistry(), &QgsLayoutItemRegistry::typeAdded, this, &QgsLayoutDesignerDialog::itemTypeAdded );

restoreWindowState();
}

Expand Down Expand Up @@ -122,6 +134,23 @@ void QgsLayoutDesignerDialog::closeEvent( QCloseEvent * )
saveWindowState();
}

void QgsLayoutDesignerDialog::itemTypeAdded( int type, const QString &name )
{
// 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() );
mToolsActionGroup->addAction( action );
mItemMenu->addAction( action );
mItemToolbar->addAction( action );
connect( action, &QAction::triggered, this, [this, type]()
{
activateNewItemCreationTool( type );
} );
}

void QgsLayoutDesignerDialog::saveWindowState()
{
QgsSettings settings;
Expand All @@ -147,4 +176,9 @@ void QgsLayoutDesignerDialog::restoreWindowState()
}
}

void QgsLayoutDesignerDialog::activateNewItemCreationTool( int type )
{
QgsLogger::debug( QStringLiteral( "creating new %1 item " ).arg( QgsApplication::layoutItemRegistry()->itemMetadata( type )->visibleName() ) );
}


9 changes: 9 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.h
Expand Up @@ -96,18 +96,27 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner

virtual void closeEvent( QCloseEvent * ) override;

private slots:

void itemTypeAdded( int type, const QString &name );

private:

QgsAppLayoutDesignerInterface *mInterface = nullptr;

QgsLayout *mLayout = nullptr;

QActionGroup *mToolsActionGroup = nullptr;


//! Save window state
void saveWindowState();

//! Restore the window and toolbar state
void restoreWindowState();

//! Switch to new item creation tool, for a new item of the specified \a type.
void activateNewItemCreationTool( int type );

};

Expand Down
18 changes: 14 additions & 4 deletions src/core/layout/qgslayoutitemregistry.h
Expand Up @@ -18,8 +18,10 @@

#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgsapplication.h"
#include "qgspathresolver.h"
#include <QGraphicsItem> //for QGraphicsItem::UserType
#include <QIcon>
#include <functional>

class QgsLayout;
Expand Down Expand Up @@ -51,6 +53,11 @@ class CORE_EXPORT QgsLayoutItemAbstractMetadata
*/
int type() const { return mType; }

/**
* Returns an icon representing the layout item type.
*/
virtual QIcon icon() const { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicRectangle.svg" ) ); }

/**
* Returns a translated, user visible name for the layout item class.
*/
Expand Down Expand Up @@ -112,11 +119,12 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata
* and \a visibleName, and function pointers for the various item and
* configuration widget creation functions.
*/
QgsLayoutItemMetadata( int type, const QString &visibleName,
QgsLayoutItemMetadata( int type, const QString &visibleName, const QIcon &icon,
QgsLayoutItemCreateFunc pfCreate,
QgsLayoutItemPathResolverFunc pfPathResolver = nullptr,
QgsLayoutItemWidgetFunc pfWidget = nullptr )
: QgsLayoutItemAbstractMetadata( type, visibleName )
, mIcon( icon )
, mCreateFunc( pfCreate )
, mWidgetFunc( pfWidget )
, mPathResolverFunc( pfPathResolver )
Expand Down Expand Up @@ -144,15 +152,17 @@ class CORE_EXPORT QgsLayoutItemMetadata : public QgsLayoutItemAbstractMetadata
*/
void setWidgetFunction( QgsLayoutItemWidgetFunc function ) { mWidgetFunc = function; }

virtual QgsLayoutItem *createItem( QgsLayout *layout, const QVariantMap &properties ) override { return mCreateFunc ? mCreateFunc( layout, properties ) : nullptr; }
virtual QWidget *createItemWidget() override { return mWidgetFunc ? mWidgetFunc() : nullptr; }
virtual void resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving ) override
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; }
void resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving ) override
{
if ( mPathResolverFunc )
mPathResolverFunc( properties, pathResolver, saving );
}

protected:
QIcon mIcon;
QgsLayoutItemCreateFunc mCreateFunc = nullptr;
QgsLayoutItemWidgetFunc mWidgetFunc = nullptr;
QgsLayoutItemPathResolverFunc mPathResolverFunc = nullptr;
Expand Down
8 changes: 4 additions & 4 deletions src/ui/layout/qgslayoutdesignerbase.ui
Expand Up @@ -80,19 +80,19 @@
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuLayout">
<widget class="QMenu" name="mLayoutMenu">
<property name="title">
<string>&amp;Layout</string>
</property>
<addaction name="mActionClose"/>
</widget>
<widget class="QMenu" name="menuItems">
<widget class="QMenu" name="mItemMenu">
<property name="title">
<string>&amp;Items</string>
</property>
</widget>
<addaction name="menuLayout"/>
<addaction name="menuItems"/>
<addaction name="mLayoutMenu"/>
<addaction name="mItemMenu"/>
</widget>
<action name="mActionClose">
<property name="text">
Expand Down
2 changes: 1 addition & 1 deletion tests/src/core/testqgslayoutitem.cpp
Expand Up @@ -132,7 +132,7 @@ void TestQgsLayoutItem::registry()

QSignalSpy spyTypeAdded( &registry, &QgsLayoutItemRegistry::typeAdded );

QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), create, resolve, createWidget );
QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), QIcon(), create, resolve, createWidget );
QVERIFY( registry.addLayoutItemType( metadata ) );
QCOMPARE( spyTypeAdded.count(), 1 );
QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), 2 );
Expand Down

0 comments on commit c228132

Please sign in to comment.