[feature][layouts] Add menu entry to add dynamic text labels

easily to a layout

The new "Add Item" -> "Dynamic Text" menu contains a bunch of preset
handy dynamic text expressions which users can use to insert a label
automatically containing the corresponding expression. E.g.

Add Item -> Dynamic Text -> Layout Name

will insert a label containing the expression [% @layout_name %].

This raises discoverability and user-friendliness of inserting
dynamic labels
This commit is contained in:
Nyall Dawson 2021-01-10 10:36:42 +10:00
parent f81737c960
commit ad944156c8
13 changed files with 355 additions and 29 deletions

View File

@ -188,6 +188,18 @@ QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed th
%Docstring
Returns the metadata for the specified item ``metadataId``. Returns ``None`` if
a corresponding ``metadataId`` was not found in the registry.
%End
int metadataIdForItemType( int type ) const;
%Docstring
Returns the GUI item metadata ID which corresponds to the specified layout item ``type``.
In the case that multiple GUI metadata classes exist for a single layout item ``type`` then
only the first encountered GUI metadata ID will be returned.
Returns -1 if no matching metadata is found in the GUI registry.
.. versionadded:: 3.18
%End
bool addLayoutItemGuiMetadata( QgsLayoutItemAbstractGuiMetadata *metadata /Transfer/ );
@ -219,12 +231,15 @@ Returns a reference to the item group with matching ``id``.
Creates a new instance of a layout item given the item metadata ``metadataId``, target ``layout``.
%End
void newItemAddedToLayout( int metadataId, QgsLayoutItem *item );
void newItemAddedToLayout( int metadataId, QgsLayoutItem *item, const QVariantMap &properties = QVariantMap() );
%Docstring
Called when a newly created item of the associated metadata ``metadataId`` has been added to a layout.
This is only called for additions which result from GUI operations - i.e. it is not
called for items added from templates.
Since QGIS 3.18 the optional ``properties`` argument can be used to pass custom properties to the
:py:func:`QgsLayoutItemGuiMetadata.newItemAddedToLayout()` function.
%End

View File

@ -49,9 +49,32 @@ from :py:class:`QgsLayoutItemGuiRegistry`.
virtual void layoutReleaseEvent( QgsLayoutViewMouseEvent *event );
virtual void activate();
virtual void deactivate();
QVariantMap customProperties() const;
%Docstring
Returns any custom properties set for the tool.
.. seealso:: :py:func:`setCustomProperties`
.. versionadded:: 3.18
%End
void setCustomProperties( const QVariantMap &properties );
%Docstring
Sets custom ``properties`` for the tool.
These properties are transient, and are cleared whenever the tool is activated. Callers must ensure
that the properties are set only after the tool is activated.
.. seealso:: :py:func:`customProperties`
.. versionadded:: 3.18
%End
signals:
void createdItem();

View File

@ -73,6 +73,7 @@
#include "qgsabstractvaliditycheck.h"
#include "qgsvaliditycheckcontext.h"
#include "qgsprojectviewsettings.h"
#include "qgslayoutlabelwidget.h"
#include "ui_defaults.h"
#include <QShortcut>
@ -407,6 +408,23 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionClose, &QAction::triggered, this, &QWidget::close );
mDynamicTextMenu = new QMenu( tr( "Dynamic Text" ), this );
connect( mDynamicTextMenu, &QMenu::aboutToShow, this, [ = ]
{
mDynamicTextMenu->clear();
// we need to rebuild this on each show, as the content varies depending on other available items...
QgsLayoutLabelWidget::buildInsertDynamicTextMenu( mLayout, mDynamicTextMenu, [ = ]( const QString & expression )
{
activateNewItemCreationTool( QgsGui::layoutItemGuiRegistry()->metadataIdForItemType( QgsLayoutItemRegistry::LayoutLabel ), false );
QVariantMap properties;
properties.insert( QStringLiteral( "expression" ), expression );
mAddItemTool->setCustomProperties( properties );
} );
} );
mItemMenu->addMenu( mDynamicTextMenu );
// populate with initial items...
const QList< int > itemMetadataIds = QgsGui::layoutItemGuiRegistry()->itemMetadataIds();
for ( int id : itemMetadataIds )

View File

@ -465,6 +465,8 @@ class QgsLayoutDesignerDialog: public QMainWindow, public Ui::QgsLayoutDesignerB
QAction *mActionPaste = nullptr;
QProgressBar *mStatusProgressBar = nullptr;
QMenu *mDynamicTextMenu = nullptr;
struct PanelStatus
{
PanelStatus( bool visible = true, bool active = false )

View File

@ -117,7 +117,7 @@ void QgsLayoutGuiUtils::registerGuiForKnownItemTypes( QgsMapCanvas *mapCanvas )
{
return new QgsLayoutMapWidget( qobject_cast< QgsLayoutItemMap * >( item ), mapCanvas );
}, createRubberBand );
mapItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item )
mapItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item, const QVariantMap & )
{
QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( item );
Q_ASSERT( map );
@ -176,12 +176,12 @@ void QgsLayoutGuiUtils::registerGuiForKnownItemTypes( QgsMapCanvas *mapCanvas )
{
return new QgsLayoutLabelWidget( qobject_cast< QgsLayoutItemLabel * >( item ) );
}, createRubberBand );
labelItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item )
labelItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item, const QVariantMap & properties )
{
QgsLayoutItemLabel *label = qobject_cast< QgsLayoutItemLabel * >( item );
Q_ASSERT( label );
label->setText( QObject::tr( "Lorem ipsum" ) );
label->setText( properties.value( QStringLiteral( "expression" ) ).toString().isEmpty() ? QObject::tr( "Lorem ipsum" ) : QStringLiteral( "[% %1 %]" ).arg( properties.value( QStringLiteral( "expression" ) ).toString() ) );
if ( QApplication::isRightToLeft() )
{
label->setHAlign( Qt::AlignRight );
@ -205,7 +205,7 @@ void QgsLayoutGuiUtils::registerGuiForKnownItemTypes( QgsMapCanvas *mapCanvas )
{
return new QgsLayoutLegendWidget( qobject_cast< QgsLayoutItemLegend * >( item ), mapCanvas );
}, createRubberBand );
legendItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item )
legendItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item, const QVariantMap & )
{
QgsLayoutItemLegend *legend = qobject_cast< QgsLayoutItemLegend * >( item );
Q_ASSERT( legend );
@ -246,7 +246,7 @@ void QgsLayoutGuiUtils::registerGuiForKnownItemTypes( QgsMapCanvas *mapCanvas )
{
return new QgsLayoutScaleBarWidget( qobject_cast< QgsLayoutItemScaleBar * >( item ) );
}, createRubberBand );
scalebarItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item )
scalebarItemMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item, const QVariantMap & )
{
QgsLayoutItemScaleBar *scalebar = qobject_cast< QgsLayoutItemScaleBar * >( item );
Q_ASSERT( scalebar );
@ -294,7 +294,7 @@ void QgsLayoutGuiUtils::registerGuiForKnownItemTypes( QgsMapCanvas *mapCanvas )
picture->setId( northArrowCount > 0 ? QObject::tr( "North Arrow %1" ).arg( northArrowCount + 1 ) : QObject::tr( "North Arrow" ) );
return picture.release();
} );
northArrowMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item )
northArrowMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item, const QVariantMap & )
{
QgsLayoutItemPicture *picture = qobject_cast< QgsLayoutItemPicture * >( item );
Q_ASSERT( picture );

View File

@ -57,6 +57,16 @@ QgsLayoutItemAbstractGuiMetadata *QgsLayoutItemGuiRegistry::itemMetadata( int me
return mMetadata.value( metadataId );
}
int QgsLayoutItemGuiRegistry::metadataIdForItemType( int type ) const
{
for ( auto it = mMetadata.constBegin(); it != mMetadata.constEnd(); ++it )
{
if ( it.value()->type() == type )
return it.key();
}
return -1;
}
bool QgsLayoutItemGuiRegistry::addLayoutItemGuiMetadata( QgsLayoutItemAbstractGuiMetadata *metadata )
{
if ( !metadata )
@ -95,12 +105,19 @@ QgsLayoutItem *QgsLayoutItemGuiRegistry::createItem( int metadataId, QgsLayout *
return QgsApplication::layoutItemRegistry()->createItem( type, layout );
}
void QgsLayoutItemGuiRegistry::newItemAddedToLayout( int metadataId, QgsLayoutItem *item )
void QgsLayoutItemGuiRegistry::newItemAddedToLayout( int metadataId, QgsLayoutItem *item, const QVariantMap &properties )
{
if ( !mMetadata.contains( metadataId ) )
return;
mMetadata.value( metadataId )->newItemAddedToLayout( item );
if ( QgsLayoutItemGuiMetadata *metadata = dynamic_cast<QgsLayoutItemGuiMetadata *>( mMetadata.value( metadataId ) ) )
{
metadata->newItemAddedToLayout( item, properties );
}
else
{
mMetadata.value( metadataId )->newItemAddedToLayout( item );
}
}
QgsLayoutItemBaseWidget *QgsLayoutItemGuiRegistry::createItemWidget( QgsLayoutItem *item ) const
@ -153,5 +170,11 @@ QgsLayoutItem *QgsLayoutItemGuiMetadata::createItem( QgsLayout *layout )
void QgsLayoutItemGuiMetadata::newItemAddedToLayout( QgsLayoutItem *item )
{
if ( mAddedToLayoutFunc )
mAddedToLayoutFunc( item );
mAddedToLayoutFunc( item, QVariantMap() );
}
void QgsLayoutItemGuiMetadata::newItemAddedToLayout( QgsLayoutItem *item, const QVariantMap &properties )
{
if ( mAddedToLayoutFunc )
mAddedToLayoutFunc( item, properties );
}

View File

@ -170,7 +170,7 @@ typedef std::function<QgsLayoutViewRubberBand *( QgsLayoutView * )> QgsLayoutIte
typedef std::function<QAbstractGraphicsShapeItem *( QgsLayoutView * )> QgsLayoutNodeItemRubberBandFunc SIP_SKIP;
//! Layout item added to layout callback
typedef std::function<void ( QgsLayoutItem * )> QgsLayoutItemAddedToLayoutFunc SIP_SKIP;
typedef std::function<void ( QgsLayoutItem *, const QVariantMap & )> QgsLayoutItemAddedToLayoutFunc SIP_SKIP;
#ifndef SIP_RUN
@ -272,8 +272,10 @@ class GUI_EXPORT QgsLayoutItemGuiMetadata : public QgsLayoutItemAbstractGuiMetad
QgsLayoutItemBaseWidget *createItemWidget( QgsLayoutItem *item ) override { return mWidgetFunc ? mWidgetFunc( item ) : nullptr; }
QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) override { return mRubberBandFunc ? mRubberBandFunc( view ) : nullptr; }
QAbstractGraphicsShapeItem *createNodeRubberBand( QgsLayoutView *view ) override { return mNodeRubberBandFunc ? mNodeRubberBandFunc( view ) : nullptr; }
QgsLayoutItem *createItem( QgsLayout *layout ) override;
void newItemAddedToLayout( QgsLayoutItem *item ) override;
void newItemAddedToLayout( QgsLayoutItem *item, const QVariantMap &properties );
protected:
QIcon mIcon;
@ -370,6 +372,18 @@ class GUI_EXPORT QgsLayoutItemGuiRegistry : public QObject
*/
QgsLayoutItemAbstractGuiMetadata *itemMetadata( int metadataId ) const;
/**
* Returns the GUI item metadata ID which corresponds to the specified layout item \a type.
*
* In the case that multiple GUI metadata classes exist for a single layout item \a type then
* only the first encountered GUI metadata ID will be returned.
*
* Returns -1 if no matching metadata is found in the GUI registry.
*
* \since QGIS 3.18
*/
int metadataIdForItemType( int type ) const;
/**
* Registers the gui metadata for a new layout item type. Takes ownership of the metadata instance.
*/
@ -417,8 +431,11 @@ class GUI_EXPORT QgsLayoutItemGuiRegistry : public QObject
*
* This is only called for additions which result from GUI operations - i.e. it is not
* called for items added from templates.
*
* Since QGIS 3.18 the optional \a properties argument can be used to pass custom properties to the
* QgsLayoutItemGuiMetadata::newItemAddedToLayout() function.
*/
void newItemAddedToLayout( int metadataId, QgsLayoutItem *item );
void newItemAddedToLayout( int metadataId, QgsLayoutItem *item, const QVariantMap &properties = QVariantMap() );
/*
* IMPORTANT: While it seems like /Factory/ would be the correct annotations here, that's not

View File

@ -20,10 +20,14 @@
#include "qgslayout.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsguiutils.h"
#include "qgslayoutitemmap.h"
#include "qgsvectorlayer.h"
#include <QColorDialog>
#include <QFontDialog>
#include <QWidget>
#include <QAction>
#include <QMenu>
QgsLayoutLabelWidget::QgsLayoutLabelWidget( QgsLayoutItemLabel *label )
: QgsLayoutItemBaseWidget( nullptr, label )
@ -64,6 +68,22 @@ QgsLayoutLabelWidget::QgsLayoutLabelWidget( QgsLayoutItemLabel *label )
connect( mFontButton, &QgsFontButton::changed, this, &QgsLayoutLabelWidget::fontChanged );
connect( mJustifyRadioButton, &QRadioButton::clicked, this, &QgsLayoutLabelWidget::justifyClicked );
mDynamicTextMenu = new QMenu( this );
mDynamicTextButton->setMenu( mDynamicTextMenu );
connect( mDynamicTextMenu, &QMenu::aboutToShow, this, [ = ]
{
mDynamicTextMenu->clear();
// we need to rebuild this on each show, as the content varies depending on other available items...
buildInsertDynamicTextMenu( mLabel->layout(), mDynamicTextMenu, [ = ]( const QString & expression )
{
mLabel->beginCommand( tr( "Insert dynamic text" ) );
mTextEdit->insertPlainText( "[%" + expression + "%]" );
mLabel->endCommand();
} );
} );
}
void QgsLayoutLabelWidget::setMasterLayout( QgsMasterLayoutInterface *masterLayout )
@ -72,6 +92,132 @@ void QgsLayoutLabelWidget::setMasterLayout( QgsMasterLayoutInterface *masterLayo
mItemPropertiesWidget->setMasterLayout( masterLayout );
}
void QgsLayoutLabelWidget::buildInsertDynamicTextMenu( QgsLayout *layout, QMenu *menu, const std::function<void ( const QString & )> &callback )
{
auto addExpression = [&callback]( QMenu * menu, const QString & name, const QString & expression )
{
QAction *action = new QAction( name, menu );
connect( action, &QAction::triggered, action, [callback, expression]
{
callback( expression );
} );
menu->addAction( action );
};
QMenu *dateMenu = new QMenu( tr( "Current Date" ), menu );
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "ISO Format (%1)" ).arg( QDateTime::currentDateTime().toString( QStringLiteral( "yyyy-MM-dd" ) ) ), QStringLiteral( "format_date(now(), 'yyyy-MM-dd')" ) ),
std::make_pair( tr( "Day/Month/Year (%1)" ).arg( QDateTime::currentDateTime().toString( QStringLiteral( "dd/MM/yyyy" ) ) ), QStringLiteral( "format_date(now(), 'dd/MM/yyyy')" ) ),
std::make_pair( tr( "Month/Day/Year (%1)" ).arg( QDateTime::currentDateTime().toString( QStringLiteral( "MM/dd/yyyy" ) ) ), QStringLiteral( "format_date(now(), 'MM/dd/yyyy')" ) ),
} )
{
addExpression( dateMenu, expression.first, expression.second );
}
menu->addMenu( dateMenu );
QMenu *mapsMenu = new QMenu( tr( "Map Properties" ), menu );
QList< QgsLayoutItemMap * > maps;
layout->layoutItems( maps );
for ( QgsLayoutItemMap *map : qgis::as_const( maps ) )
{
// these expressions require the map to have a non-empty ID set
if ( map->id().isEmpty() )
continue;
QMenu *mapMenu = new QMenu( map->displayName(), mapsMenu );
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "Scale (%1)" ).arg( map->scale() ), QStringLiteral( "item_variables('%1')['map_scale']" ).arg( map->id() ) ),
std::make_pair( tr( "Rotation (%1)" ).arg( map->rotation() ), QStringLiteral( "item_variables('%1')['map_rotation']" ).arg( map->id() ) ),
} )
{
addExpression( mapMenu, expression.first, expression.second );
}
mapMenu->addSeparator();
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "CRS Identifier (%1)" ).arg( map->crs().authid() ), QStringLiteral( "item_variables('%1')['map_crs']" ).arg( map->id() ) ),
std::make_pair( tr( "CRS Name (%1)" ).arg( map->crs().description() ), QStringLiteral( "item_variables('%1')['map_crs_description']" ).arg( map->id() ) ),
std::make_pair( tr( "Ellipsoid Name (%1)" ).arg( map->crs().ellipsoidAcronym() ), QStringLiteral( "item_variables('%1')['map_crs_ellipsoid']" ).arg( map->id() ) ),
std::make_pair( tr( "Units (%1)" ).arg( QgsUnitTypes::toString( map->crs().mapUnits() ) ), QStringLiteral( "item_variables('%1')['map_units']" ).arg( map->id() ) ),
} )
{
addExpression( mapMenu, expression.first, expression.second );
}
mapMenu->addSeparator();
const QgsRectangle mapExtent = map->extent();
const QgsPointXY center = mapExtent.center();
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "Center (X) (%1)" ).arg( center.x() ), QStringLiteral( "x(item_variables('%1')['map_extent_center'])" ).arg( map->id() ) ),
std::make_pair( tr( "Center (Y) (%1)" ).arg( center.y() ), QStringLiteral( "y(item_variables('%1')['map_extent_center'])" ).arg( map->id() ) ),
std::make_pair( tr( "X Minimum (%1)" ).arg( mapExtent.xMinimum() ), QStringLiteral( "x_min(item_variables('%1')['map_extent'])" ).arg( map->id() ) ),
std::make_pair( tr( "Y Minimum (%1)" ).arg( mapExtent.yMinimum() ), QStringLiteral( "y_min(item_variables('%1')['map_extent'])" ).arg( map->id() ) ),
std::make_pair( tr( "X Maximum (%1)" ).arg( mapExtent.xMaximum() ), QStringLiteral( "x_max(item_variables('%1')['map_extent'])" ).arg( map->id() ) ),
std::make_pair( tr( "Y Maximum (%1)" ).arg( mapExtent.yMaximum() ), QStringLiteral( "y_max(item_variables('%1')['map_extent'])" ).arg( map->id() ) ),
} )
{
addExpression( mapMenu, expression.first, expression.second );
}
mapMenu->addSeparator();
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "Layer Credits" ), QStringLiteral( "array_to_string(map_credits('%1'))" ).arg( map->id() ) ),
} )
{
addExpression( mapMenu, expression.first, expression.second );
}
mapsMenu->addMenu( mapMenu );
}
menu->addMenu( mapsMenu );
menu->addSeparator();
if ( layout->reportContext().layer() )
{
const QgsFields fields = layout->reportContext().layer()->fields();
QMenu *fieldsMenu = new QMenu( tr( "Field" ), menu );
for ( const QgsField &field : fields )
{
addExpression( fieldsMenu, field.displayName(), QStringLiteral( "\"%1\"" ).arg( field.name() ) );
}
menu->addMenu( fieldsMenu );
menu->addSeparator();
}
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "Layout Name" ), QStringLiteral( "@layout_name" ) ),
std::make_pair( tr( "Layout Page Number" ), QStringLiteral( "@layout_page" ) ),
std::make_pair( tr( "Layout Page Count" ), QStringLiteral( "@layout_numpages" ) )
} )
{
addExpression( menu, expression.first, expression.second );
}
menu->addSeparator();
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "Project Author" ), QStringLiteral( "@project_author" ) ),
std::make_pair( tr( "Project Title" ), QStringLiteral( "@project_title" ) ),
std::make_pair( tr( "Project Path" ), QStringLiteral( "@project_path" ) )
} )
{
addExpression( menu, expression.first, expression.second );
}
menu->addSeparator();
for ( const std::pair< QString, QString > &expression :
{
std::make_pair( tr( "Current User Name" ), QStringLiteral( "@user_full_name" ) ),
std::make_pair( tr( "Current User Account" ), QStringLiteral( "@user_account_name" ) )
} )
{
addExpression( menu, expression.first, expression.second );
}
}
bool QgsLayoutLabelWidget::setNewItem( QgsLayoutItem *item )
{
if ( item->type() != QgsLayoutItemRegistry::LayoutLabel )

View File

@ -25,6 +25,7 @@
#include "ui_qgslayoutlabelwidgetbase.h"
#include "qgslayoutitemwidget.h"
#include "qgslayoutitemlabel.h"
#include <functional>
/**
* \ingroup gui
@ -41,6 +42,22 @@ class GUI_EXPORT QgsLayoutLabelWidget: public QgsLayoutItemBaseWidget, private U
explicit QgsLayoutLabelWidget( QgsLayoutItemLabel *label );
void setMasterLayout( QgsMasterLayoutInterface *masterLayout ) override;
/**
* Populates the specified \a menu with actions reflecting dynamic text expressions applicable for a \a layout.
*
* This includes dynamic text for expressions like:
*
* - current date
* - total page count
* - current page number
* - etc
*
* The \a callback function will be called whenever one of the created actions is triggered.
*
* \since QGIS 3.18
*/
static void buildInsertDynamicTextMenu( QgsLayout *layout, QMenu *menu, const std::function< void( const QString &expression ) > &callback );
protected:
bool setNewItem( QgsLayoutItem *item ) override;
@ -67,6 +84,8 @@ class GUI_EXPORT QgsLayoutLabelWidget: public QgsLayoutItemBaseWidget, private U
QgsLayoutItemPropertiesWidget *mItemPropertiesWidget = nullptr;
QMenu *mDynamicTextMenu = nullptr;
void blockAllSignals( bool block );
};

View File

@ -146,7 +146,7 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even
settings.setEnumValue( QStringLiteral( "LayoutDesigner/lastSizeUnit" ), item->sizeWithUnits().units() );
}
QgsGui::layoutItemGuiRegistry()->newItemAddedToLayout( mItemMetadataId, item );
QgsGui::layoutItemGuiRegistry()->newItemAddedToLayout( mItemMetadataId, item, mCustomProperties );
// it's possible (in certain circumstances, e.g. when adding frame items) that this item
// has already been added to the layout
@ -158,6 +158,12 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even
emit createdItem();
}
void QgsLayoutViewToolAddItem::activate()
{
QgsLayoutViewTool::activate();
mCustomProperties.clear();
}
void QgsLayoutViewToolAddItem::deactivate()
{
if ( mDrawing )
@ -170,6 +176,16 @@ void QgsLayoutViewToolAddItem::deactivate()
QgsLayoutViewTool::deactivate();
}
QVariantMap QgsLayoutViewToolAddItem::customProperties() const
{
return mCustomProperties;
}
void QgsLayoutViewToolAddItem::setCustomProperties( const QVariantMap &customProperties )
{
mCustomProperties = customProperties;
}
int QgsLayoutViewToolAddItem::itemMetadataId() const
{
return mItemMetadataId;

View File

@ -56,8 +56,28 @@ class GUI_EXPORT QgsLayoutViewToolAddItem : public QgsLayoutViewTool
void layoutPressEvent( QgsLayoutViewMouseEvent *event ) override;
void layoutMoveEvent( QgsLayoutViewMouseEvent *event ) override;
void layoutReleaseEvent( QgsLayoutViewMouseEvent *event ) override;
void activate() override;
void deactivate() override;
/**
* Returns any custom properties set for the tool.
*
*\see setCustomProperties()
* \since QGIS 3.18
*/
QVariantMap customProperties() const;
/**
* Sets custom \a properties for the tool.
*
* These properties are transient, and are cleared whenever the tool is activated. Callers must ensure
* that the properties are set only after the tool is activated.
*
*\see customProperties()
* \since QGIS 3.18
*/
void setCustomProperties( const QVariantMap &properties );
signals:
/**
@ -83,6 +103,8 @@ class GUI_EXPORT QgsLayoutViewToolAddItem : public QgsLayoutViewTool
//! Start of rubber band creation
QPointF mRubberBandStartPos;
QVariantMap mCustomProperties;
};
#endif // QGSLAYOUTVIEWTOOLADDITEM_H

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>304</width>
<width>360</width>
<height>705</height>
</rect>
</property>
@ -61,8 +61,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>446</width>
<height>665</height>
<width>358</width>
<height>680</height>
</rect>
</property>
<layout class="QVBoxLayout" name="mainLayout">
@ -84,27 +84,49 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="mTextEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>150</height>
</size>
<item row="2" column="1">
<widget class="QToolButton" name="mDynamicTextButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Dynamic Text</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QToolButton" name="mInsertExpressionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Insert/Edit Expression…</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="mHtmlCheckBox">
<property name="text">
<string>Render as HTML</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="mInsertExpressionButton">
<property name="text">
<string>Insert or Edit an Expression…</string>
<item row="0" column="0" colspan="2">
<widget class="QPlainTextEdit" name="mTextEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>150</height>
</size>
</property>
</widget>
</item>
@ -401,7 +423,7 @@
<resources/>
<connections/>
<buttongroups>
<buttongroup name="buttonGroup"/>
<buttongroup name="buttonGroup_2"/>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>

View File

@ -264,6 +264,7 @@ void TestQgsLayoutView::guiRegistry()
// empty registry
QVERIFY( !registry.itemMetadata( -1 ) );
QVERIFY( registry.itemMetadataIds().isEmpty() );
QCOMPARE( registry.metadataIdForItemType( 0 ), -1 );
QVERIFY( !registry.createItemWidget( nullptr ) );
QVERIFY( !registry.createItemWidget( nullptr ) );
std::unique_ptr< TestItem > testItem = qgis::make_unique< TestItem >( nullptr );
@ -287,6 +288,7 @@ void TestQgsLayoutView::guiRegistry()
QCOMPARE( spyTypeAdded.count(), 1 );
int uuid = registry.itemMetadataIds().value( 0 );
QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), uuid );
QCOMPARE( registry.metadataIdForItemType( QgsLayoutItemRegistry::LayoutItem + 101 ), uuid );
// duplicate type id is allowed
metadata = new QgsLayoutItemGuiMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "mytype" ), QIcon(), createWidget, createRubberBand );
@ -295,6 +297,7 @@ void TestQgsLayoutView::guiRegistry()
//retrieve metadata
QVERIFY( !registry.itemMetadata( -1 ) );
QCOMPARE( registry.itemMetadataIds().count(), 2 );
QCOMPARE( registry.metadataIdForItemType( QgsLayoutItemRegistry::LayoutItem + 101 ), uuid );
QVERIFY( registry.itemMetadata( uuid ) );
QCOMPARE( registry.itemMetadata( uuid )->visibleName(), QStringLiteral( "mytype" ) );