Skip to content

Commit 0939333

Browse files
committedOct 6, 2017
Port item alignment to layouts
1 parent c6da276 commit 0939333

File tree

10 files changed

+393
-2
lines changed

10 files changed

+393
-2
lines changed
 

‎python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
%Include composer/qgscomposermultiframecommand.sip
158158
%Include composer/qgscomposertexttable.sip
159159
%Include composer/qgspaperitem.sip
160+
%Include layout/qgslayoutaligner.sip
160161
%Include layout/qgslayoutcontext.sip
161162
%Include layout/qgslayoutgridsettings.sip
162163
%Include layout/qgslayoutmeasurement.sip
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/layout/qgslayoutaligner.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
class QgsLayoutAligner
12+
{
13+
%Docstring
14+
Handles aligning and distributing sets of layout items.
15+
16+
QgsLayoutAligner contains methods for automatically aligning and distributing
17+
sets of layout items, e.g. aligning a group of items to top or left sides.
18+
19+
.. versionadded:: 3.0
20+
%End
21+
22+
%TypeHeaderCode
23+
#include "qgslayoutaligner.h"
24+
%End
25+
public:
26+
27+
enum Alignment
28+
{
29+
Left,
30+
HCenter,
31+
Right,
32+
Top,
33+
VCenter,
34+
Bottom,
35+
};
36+
37+
static void alignItems( QgsLayout *layout, const QList< QgsLayoutItem * > &items, Alignment alignment );
38+
%Docstring
39+
Aligns a set of ``items`` from a ``layout`` in place.
40+
41+
The ``alignment`` argument specifies the method to use when aligning the items.
42+
%End
43+
44+
};
45+
46+
/************************************************************************
47+
* This file has been generated automatically from *
48+
* *
49+
* src/core/layout/qgslayoutaligner.h *
50+
* *
51+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
52+
************************************************************************/

‎python/gui/layout/qgslayoutview.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ class QgsLayoutView: QGraphicsView
127127
:rtype: list of int
128128
%End
129129

130+
void alignSelectedItems( QgsLayoutAligner::Alignment alignment );
131+
%Docstring
132+
Aligns all selected items using the specified ``alignment``.
133+
%End
134+
130135
public slots:
131136

132137
void zoomFull();

‎src/app/layout/qgslayoutdesignerdialog.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,20 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
179179
orderingToolButton->setDefaultAction( mActionRaiseItems );
180180
mActionsToolbar->addWidget( orderingToolButton );
181181

182+
QToolButton *alignToolButton = new QToolButton( this );
183+
alignToolButton->setPopupMode( QToolButton::InstantPopup );
184+
alignToolButton->setAutoRaise( true );
185+
alignToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
186+
187+
alignToolButton->addAction( mActionAlignLeft );
188+
alignToolButton->addAction( mActionAlignHCenter );
189+
alignToolButton->addAction( mActionAlignRight );
190+
alignToolButton->addAction( mActionAlignTop );
191+
alignToolButton->addAction( mActionAlignVCenter );
192+
alignToolButton->addAction( mActionAlignBottom );
193+
alignToolButton->setDefaultAction( mActionAlignLeft );
194+
mActionsToolbar->addWidget( alignToolButton );
195+
182196
mAddItemTool = new QgsLayoutViewToolAddItem( mView );
183197
mPanTool = new QgsLayoutViewToolPan( mView );
184198
mPanTool->setAction( mActionPan );
@@ -213,6 +227,30 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
213227
connect( mActionLowerItems, &QAction::triggered, this, &QgsLayoutDesignerDialog::lowerSelectedItems );
214228
connect( mActionMoveItemsToTop, &QAction::triggered, this, &QgsLayoutDesignerDialog::moveSelectedItemsToTop );
215229
connect( mActionMoveItemsToBottom, &QAction::triggered, this, &QgsLayoutDesignerDialog::moveSelectedItemsToBottom );
230+
connect( mActionAlignLeft, &QAction::triggered, this, [ = ]
231+
{
232+
mView->alignSelectedItems( QgsLayoutAligner::Left );
233+
} );
234+
connect( mActionAlignHCenter, &QAction::triggered, this, [ = ]
235+
{
236+
mView->alignSelectedItems( QgsLayoutAligner::HCenter );
237+
} );
238+
connect( mActionAlignRight, &QAction::triggered, this, [ = ]
239+
{
240+
mView->alignSelectedItems( QgsLayoutAligner::Right );
241+
} );
242+
connect( mActionAlignTop, &QAction::triggered, this, [ = ]
243+
{
244+
mView->alignSelectedItems( QgsLayoutAligner::Top );
245+
} );
246+
connect( mActionAlignVCenter, &QAction::triggered, this, [ = ]
247+
{
248+
mView->alignSelectedItems( QgsLayoutAligner::VCenter );
249+
} );
250+
connect( mActionAlignBottom, &QAction::triggered, this, [ = ]
251+
{
252+
mView->alignSelectedItems( QgsLayoutAligner::Bottom );
253+
} );
216254

217255
connect( mActionAddPages, &QAction::triggered, this, &QgsLayoutDesignerDialog::addPages );
218256

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ SET(QGIS_CORE_SRCS
359359
dxf/qgsdxfpallabeling.cpp
360360

361361
layout/qgslayout.cpp
362+
layout/qgslayoutaligner.cpp
362363
layout/qgslayoutcontext.cpp
363364
layout/qgslayoutgridsettings.cpp
364365
layout/qgslayoutguidecollection.cpp
@@ -965,6 +966,7 @@ SET(QGIS_CORE_HDRS
965966
composer/qgscomposertexttable.h
966967
composer/qgspaperitem.h
967968

969+
layout/qgslayoutaligner.h
968970
layout/qgslayoutcontext.h
969971
layout/qgslayoutgridsettings.h
970972
layout/qgslayoutitemundocommand.h

‎src/core/layout/qgslayoutaligner.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/***************************************************************************
2+
qgslayoutaligner.cpp
3+
--------------------
4+
begin : October 2017
5+
copyright : (C) 2017 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
/***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
17+
#include "qgslayoutaligner.h"
18+
#include "qgslayoutitem.h"
19+
#include "qgslayout.h"
20+
21+
void QgsLayoutAligner::alignItems( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Alignment alignment )
22+
{
23+
if ( !layout || items.size() < 2 )
24+
{
25+
return;
26+
}
27+
28+
QRectF itemBBox = boundingRectOfItems( items );
29+
if ( !itemBBox.isValid() )
30+
{
31+
return;
32+
}
33+
34+
double refCoord = 0;
35+
switch ( alignment )
36+
{
37+
case Left:
38+
refCoord = itemBBox.left();
39+
break;
40+
case HCenter:
41+
refCoord = itemBBox.center().x();
42+
break;
43+
case Right:
44+
refCoord = itemBBox.right();
45+
break;
46+
case Top:
47+
refCoord = itemBBox.top();
48+
break;
49+
case VCenter:
50+
refCoord = itemBBox.center().y();
51+
break;
52+
case Bottom:
53+
refCoord = itemBBox.bottom();
54+
break;
55+
}
56+
57+
layout->undoStack()->beginMacro( QObject::tr( "Aligned items bottom" ) );
58+
for ( QgsLayoutItem *item : items )
59+
{
60+
layout->undoStack()->beginCommand( item, QString() );
61+
62+
QPointF shifted = item->pos();
63+
switch ( alignment )
64+
{
65+
case Left:
66+
shifted.setX( refCoord );
67+
break;
68+
case HCenter:
69+
shifted.setX( refCoord - item->rect().width() / 2.0 );
70+
break;
71+
case Right:
72+
shifted.setX( refCoord - item->rect().width() );
73+
break;
74+
case Top:
75+
shifted.setY( refCoord );
76+
break;
77+
case VCenter:
78+
shifted.setY( refCoord - item->rect().height() / 2.0 );
79+
break;
80+
case Bottom:
81+
shifted.setY( refCoord - item->rect().height() );
82+
break;
83+
}
84+
85+
// need to keep item units
86+
QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
87+
item->attemptMove( newPos );
88+
89+
layout->undoStack()->endCommand();
90+
}
91+
layout->undoStack()->endMacro();
92+
}
93+
94+
QRectF QgsLayoutAligner::boundingRectOfItems( const QList<QgsLayoutItem *> &items )
95+
{
96+
if ( items.empty() )
97+
{
98+
return QRectF();
99+
}
100+
101+
auto it = items.constBegin();
102+
//set the box to the first item
103+
QgsLayoutItem *currentItem = *it;
104+
it++;
105+
double minX = currentItem->pos().x();
106+
double minY = currentItem->pos().y();
107+
double maxX = minX + currentItem->rect().width();
108+
double maxY = minY + currentItem->rect().height();
109+
110+
double currentMinX, currentMinY, currentMaxX, currentMaxY;
111+
112+
for ( ; it != items.constEnd(); ++it )
113+
{
114+
currentItem = *it;
115+
currentMinX = currentItem->pos().x();
116+
currentMinY = currentItem->pos().y();
117+
currentMaxX = currentMinX + currentItem->rect().width();
118+
currentMaxY = currentMinY + currentItem->rect().height();
119+
120+
if ( currentMinX < minX )
121+
minX = currentMinX;
122+
if ( currentMaxX > maxX )
123+
maxX = currentMaxX;
124+
if ( currentMinY < minY )
125+
minY = currentMinY;
126+
if ( currentMaxY > maxY )
127+
maxY = currentMaxY;
128+
}
129+
130+
return QRectF( QPointF( minX, minY ), QPointF( maxX, maxY ) );
131+
}

‎src/core/layout/qgslayoutaligner.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/***************************************************************************
2+
qgslayoutaligner.h
3+
------------------
4+
begin : October 2017
5+
copyright : (C) 2017 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
/***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSLAYOUTALIGNER_H
17+
#define QGSLAYOUTALIGNER_H
18+
19+
#include "qgis_core.h"
20+
#include <QList>
21+
#include <QRectF>
22+
23+
class QgsLayoutItem;
24+
class QgsLayout;
25+
26+
/**
27+
* \ingroup core
28+
* \class QgsLayoutAligner
29+
* \brief Handles aligning and distributing sets of layout items.
30+
*
31+
* QgsLayoutAligner contains methods for automatically aligning and distributing
32+
* sets of layout items, e.g. aligning a group of items to top or left sides.
33+
*
34+
* \since QGIS 3.0
35+
*/
36+
class CORE_EXPORT QgsLayoutAligner
37+
{
38+
39+
public:
40+
41+
//! Alignment options
42+
enum Alignment
43+
{
44+
Left, //!< Align left edges
45+
HCenter, //!< Align horizontal centers
46+
Right, //!< Align right edges
47+
Top, //!< Align top edges
48+
VCenter, //!< Align vertical centers
49+
Bottom, //!< Align bottom edges
50+
};
51+
52+
/**
53+
* Aligns a set of \a items from a \a layout in place.
54+
*
55+
* The \a alignment argument specifies the method to use when aligning the items.
56+
*/
57+
static void alignItems( QgsLayout *layout, const QList< QgsLayoutItem * > &items, Alignment alignment );
58+
59+
private:
60+
61+
/**
62+
* Returns the bounding rectangle of the selected items in
63+
* scene coordinates.
64+
*/
65+
static QRectF boundingRectOfItems( const QList< QgsLayoutItem * > &items );
66+
67+
68+
69+
};
70+
71+
#endif //QGSLAYOUTALIGNER_H

‎src/gui/layout/qgslayoutview.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ QList<int> QgsLayoutView::visiblePageNumbers() const
230230
return currentLayout()->pageCollection()->visiblePageNumbers( visibleRect );
231231
}
232232

233+
void QgsLayoutView::alignSelectedItems( QgsLayoutAligner::Alignment alignment )
234+
{
235+
const QList<QgsLayoutItem *> selectedItems = currentLayout()->selectedLayoutItems();
236+
QgsLayoutAligner::alignItems( currentLayout(), selectedItems, alignment );
237+
}
238+
233239
void QgsLayoutView::zoomFull()
234240
{
235241
fitInView( scene()->sceneRect(), Qt::KeepAspectRatio );

‎src/gui/layout/qgslayoutview.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "qgsprevieweffect.h" // for QgsPreviewEffect::PreviewMode
2222
#include "qgis_gui.h"
2323
#include "qgslayoutitempage.h"
24+
#include "qgslayoutaligner.h"
2425
#include <QPointer>
2526
#include <QGraphicsView>
2627
#include <QGraphicsRectItem>
@@ -157,6 +158,11 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
157158
*/
158159
QList< int > visiblePageNumbers() const;
159160

161+
/**
162+
* Aligns all selected items using the specified \a alignment.
163+
*/
164+
void alignSelectedItems( QgsLayoutAligner::Alignment alignment );
165+
160166
public slots:
161167

162168
/**

‎src/ui/layout/qgslayoutdesignerbase.ui

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
<x>0</x>
8686
<y>0</y>
8787
<width>1083</width>
88-
<height>25</height>
88+
<height>42</height>
8989
</rect>
9090
</property>
9191
<widget class="QMenu" name="mLayoutMenu">
@@ -160,8 +160,16 @@
160160
<addaction name="mActionLowerItems"/>
161161
<addaction name="mActionMoveItemsToTop"/>
162162
<addaction name="mActionMoveItemsToBottom"/>
163+
<addaction name="separator"/>
163164
<addaction name="mActionLockItems"/>
164165
<addaction name="mActionUnlockAll"/>
166+
<addaction name="separator"/>
167+
<addaction name="mActionAlignLeft"/>
168+
<addaction name="mActionAlignHCenter"/>
169+
<addaction name="mActionAlignRight"/>
170+
<addaction name="mActionAlignTop"/>
171+
<addaction name="mActionAlignVCenter"/>
172+
<addaction name="mActionAlignBottom"/>
165173
</widget>
166174
<addaction name="mLayoutMenu"/>
167175
<addaction name="menuEdit"/>
@@ -663,6 +671,78 @@
663671
<string>Ctrl+Shift+[</string>
664672
</property>
665673
</action>
674+
<action name="mActionAlignLeft">
675+
<property name="icon">
676+
<iconset resource="../../../images/images.qrc">
677+
<normaloff>:/images/themes/default/mActionAlignLeft.svg</normaloff>:/images/themes/default/mActionAlignLeft.svg</iconset>
678+
</property>
679+
<property name="text">
680+
<string>Align Left</string>
681+
</property>
682+
<property name="toolTip">
683+
<string>Align selected items left</string>
684+
</property>
685+
</action>
686+
<action name="mActionAlignHCenter">
687+
<property name="icon">
688+
<iconset resource="../../../images/images.qrc">
689+
<normaloff>:/images/themes/default/mActionAlignHCenter.svg</normaloff>:/images/themes/default/mActionAlignHCenter.svg</iconset>
690+
</property>
691+
<property name="text">
692+
<string>Align Center</string>
693+
</property>
694+
<property name="toolTip">
695+
<string>Align center horizontal</string>
696+
</property>
697+
</action>
698+
<action name="mActionAlignRight">
699+
<property name="icon">
700+
<iconset resource="../../../images/images.qrc">
701+
<normaloff>:/images/themes/default/mActionAlignRight.svg</normaloff>:/images/themes/default/mActionAlignRight.svg</iconset>
702+
</property>
703+
<property name="text">
704+
<string>Align Right</string>
705+
</property>
706+
<property name="toolTip">
707+
<string>Align selected items right</string>
708+
</property>
709+
</action>
710+
<action name="mActionAlignTop">
711+
<property name="icon">
712+
<iconset resource="../../../images/images.qrc">
713+
<normaloff>:/images/themes/default/mActionAlignTop.svg</normaloff>:/images/themes/default/mActionAlignTop.svg</iconset>
714+
</property>
715+
<property name="text">
716+
<string>Align Top</string>
717+
</property>
718+
<property name="toolTip">
719+
<string>Align selected items to top</string>
720+
</property>
721+
</action>
722+
<action name="mActionAlignVCenter">
723+
<property name="icon">
724+
<iconset resource="../../../images/images.qrc">
725+
<normaloff>:/images/themes/default/mActionAlignVCenter.svg</normaloff>:/images/themes/default/mActionAlignVCenter.svg</iconset>
726+
</property>
727+
<property name="text">
728+
<string>Align Center Vertical</string>
729+
</property>
730+
<property name="toolTip">
731+
<string>Align center vertical</string>
732+
</property>
733+
</action>
734+
<action name="mActionAlignBottom">
735+
<property name="icon">
736+
<iconset resource="../../../images/images.qrc">
737+
<normaloff>:/images/themes/default/mActionAlignBottom.svg</normaloff>:/images/themes/default/mActionAlignBottom.svg</iconset>
738+
</property>
739+
<property name="text">
740+
<string>Align Bottom</string>
741+
</property>
742+
<property name="toolTip">
743+
<string>Align selected items bottom</string>
744+
</property>
745+
</action>
666746
</widget>
667747
<resources>
668748
<include location="../../../images/images.qrc"/>
@@ -691,7 +771,6 @@
691771
<include location="../../../images/images.qrc"/>
692772
<include location="../../../images/images.qrc"/>
693773
<include location="../../../images/images.qrc"/>
694-
<include location="../../../images/images.qrc"/>
695774
</resources>
696775
<connections/>
697776
</ui>

0 commit comments

Comments
 (0)
Please sign in to comment.