Skip to content

Commit 0ccc8f1

Browse files
nyalldawsonnirvn
authored andcommittedJun 14, 2019
[FEATURE][layouts] Add a dedicated toolbar action to create north arrows
This is a shortcut to adding a picture item, setting it to a north arrow picture, and linking it with a map. The end result is identical, but it's much easier for new users to understand if we expose it as an explicit "North Arrow" item. Even experienced users will likely appreciate the improved workflow, including automatically linking the picture rotation to a sensible default map choice (if a map is selected, it's used. If not, the topmost map item under the newly drawn north arrow is used. If there's none, the layout's 'reference map' (or biggest map) is used as a fallback) Fixes #30162
1 parent 26c83da commit 0ccc8f1

File tree

2 files changed

+70
-42
lines changed

2 files changed

+70
-42
lines changed
 

‎images/images.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@
756756
<file>themes/default/mActionNewVirtualLayer.svg</file>
757757
<file>themes/default/mActionDoubleArrowRight.svg</file>
758758
<file>themes/default/mActionDoubleArrowLeft.svg</file>
759+
<file>north_arrows/layout_default_north_arrow.svg</file>
759760
</qresource>
760761
<qresource prefix="/images/tips">
761762
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>

‎src/app/layout/qgslayoutapputils.cpp

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,45 @@
4747
#include "qgslayout3dmapwidget.h"
4848
#endif
4949

50+
/**
51+
* Attempts to find the best guess at a map item to link \a referenceItem to,
52+
* by:
53+
* 1. Prioritising a selected map
54+
* 2. If no selection, prioritising the topmost map the item was drawn over
55+
* 3. If still none, use the layout's reference map (or biggest map)
56+
*/
57+
QgsLayoutItemMap *findSensibleDefaultLinkedMapItem( QgsLayoutItem *referenceItem )
58+
{
59+
// start by trying to find a selected map
60+
QList<QgsLayoutItemMap *> mapItems;
61+
referenceItem->layout()->layoutItems( mapItems );
62+
63+
QgsLayoutItemMap *targetMap = nullptr;
64+
for ( QgsLayoutItemMap *map : qgis::as_const( mapItems ) )
65+
{
66+
if ( map->isSelected() )
67+
{
68+
return map;
69+
}
70+
}
71+
72+
// nope, no selection... hm, was the item drawn over a map? If so, use the topmost intersecting one
73+
double largestZValue = std::numeric_limits< double >::lowest();
74+
for ( QgsLayoutItemMap *map : qgis::as_const( mapItems ) )
75+
{
76+
if ( map->collidesWithItem( referenceItem ) && map->zValue() > largestZValue )
77+
{
78+
targetMap = map;
79+
largestZValue = map->zValue();
80+
}
81+
}
82+
if ( targetMap )
83+
return targetMap;
84+
85+
// ah frick it, just use the reference (or biggest!) map
86+
return referenceItem->layout()->referenceMap();
87+
}
88+
5089
void QgsLayoutAppUtils::registerGuiForKnownItemTypes()
5190
{
5291
QgsLayoutItemGuiRegistry *registry = QgsGui::layoutItemGuiRegistry();
@@ -153,29 +192,8 @@ void QgsLayoutAppUtils::registerGuiForKnownItemTypes()
153192
QgsLayoutItemLegend *legend = qobject_cast< QgsLayoutItemLegend * >( item );
154193
Q_ASSERT( legend );
155194

156-
QList<QgsLayoutItemMap *> mapItems;
157-
legend->layout()->layoutItems( mapItems );
158-
159195
// try to find a good map to link the legend with by default
160-
// start by trying to find a selected map
161-
QgsLayoutItemMap *targetMap = nullptr;
162-
for ( QgsLayoutItemMap *map : qgis::as_const( mapItems ) )
163-
{
164-
if ( map->isSelected() )
165-
{
166-
targetMap = map;
167-
break;
168-
}
169-
}
170-
// otherwise just use first map
171-
if ( !targetMap && !mapItems.isEmpty() )
172-
{
173-
targetMap = mapItems.at( 0 );
174-
}
175-
if ( targetMap )
176-
{
177-
legend->setLinkedMap( targetMap );
178-
}
196+
legend->setLinkedMap( findSensibleDefaultLinkedMapItem( legend ) );
179197

180198
legend->updateLegend();
181199
} );
@@ -194,26 +212,8 @@ void QgsLayoutAppUtils::registerGuiForKnownItemTypes()
194212
QgsLayoutItemScaleBar *scalebar = qobject_cast< QgsLayoutItemScaleBar * >( item );
195213
Q_ASSERT( scalebar );
196214

197-
QList<QgsLayoutItemMap *> mapItems;
198-
scalebar->layout()->layoutItems( mapItems );
199-
200215
// try to find a good map to link the scalebar with by default
201-
// start by trying to find a selected map
202-
QgsLayoutItemMap *targetMap = nullptr;
203-
for ( QgsLayoutItemMap *map : qgis::as_const( mapItems ) )
204-
{
205-
if ( map->isSelected() )
206-
{
207-
targetMap = map;
208-
break;
209-
}
210-
}
211-
// otherwise just use first map
212-
if ( !targetMap && !mapItems.isEmpty() )
213-
{
214-
targetMap = mapItems.at( 0 );
215-
}
216-
if ( targetMap )
216+
if ( QgsLayoutItemMap *targetMap = findSensibleDefaultLinkedMapItem( scalebar ) )
217217
{
218218
scalebar->setLinkedMap( targetMap );
219219
scalebar->applyDefaultSize( scalebar->guessUnits() );
@@ -222,6 +222,34 @@ void QgsLayoutAppUtils::registerGuiForKnownItemTypes()
222222

223223
registry->addLayoutItemGuiMetadata( scalebarItemMetadata.release() );
224224

225+
226+
// north arrow
227+
std::unique_ptr< QgsLayoutItemGuiMetadata > northArrowMetadata = qgis::make_unique< QgsLayoutItemGuiMetadata>(
228+
QgsLayoutItemRegistry::LayoutPicture, QObject::tr( "North Arrow" ), QgsApplication::getThemeIcon( QStringLiteral( "/north_arrow.svg" ) ),
229+
[ = ]( QgsLayoutItem * item )->QgsLayoutItemBaseWidget *
230+
{
231+
return new QgsLayoutPictureWidget( qobject_cast< QgsLayoutItemPicture * >( item ) );
232+
}, createRubberBand );
233+
northArrowMetadata->setItemCreationFunction( []( QgsLayout * layout )->QgsLayoutItem *
234+
{
235+
std::unique_ptr< QgsLayoutItemPicture > picture = qgis::make_unique< QgsLayoutItemPicture >( layout );
236+
picture->setNorthMode( QgsLayoutItemPicture::GridNorth );
237+
picture->setPicturePath( QStringLiteral( ":/images/north_arrows/layout_default_north_arrow.svg" ) );
238+
return picture.release();
239+
} );
240+
northArrowMetadata->setItemAddedToLayoutFunction( [ = ]( QgsLayoutItem * item )
241+
{
242+
QgsLayoutItemPicture *picture = qobject_cast< QgsLayoutItemPicture * >( item );
243+
Q_ASSERT( picture );
244+
245+
QList<QgsLayoutItemMap *> mapItems;
246+
picture->layout()->layoutItems( mapItems );
247+
248+
// try to find a good map to link the north arrow with by default
249+
picture->setLinkedMap( findSensibleDefaultLinkedMapItem( picture ) );
250+
} );
251+
registry->addLayoutItemGuiMetadata( northArrowMetadata.release() );
252+
225253
// shape items
226254

227255
auto createShapeWidget =
@@ -271,7 +299,6 @@ void QgsLayoutAppUtils::registerGuiForKnownItemTypes()
271299
} );
272300
registry->addLayoutItemGuiMetadata( arrowMetadata.release() );
273301

274-
275302
// node items
276303

277304
std::unique_ptr< QgsLayoutItemGuiMetadata > polygonMetadata = qgis::make_unique< QgsLayoutItemGuiMetadata >(

0 commit comments

Comments
 (0)
Please sign in to comment.