@@ -34,50 +34,50 @@ void QgsLayoutAligner::alignItems( QgsLayout *layout, const QList<QgsLayoutItem
3434 double refCoord = 0 ;
3535 switch ( alignment )
3636 {
37- case Left :
37+ case AlignLeft :
3838 refCoord = itemBBox.left ();
3939 break ;
40- case HCenter :
40+ case AlignHCenter :
4141 refCoord = itemBBox.center ().x ();
4242 break ;
43- case Right :
43+ case AlignRight :
4444 refCoord = itemBBox.right ();
4545 break ;
46- case Top :
46+ case AlignTop :
4747 refCoord = itemBBox.top ();
4848 break ;
49- case VCenter :
49+ case AlignVCenter :
5050 refCoord = itemBBox.center ().y ();
5151 break ;
52- case Bottom :
52+ case AlignBottom :
5353 refCoord = itemBBox.bottom ();
5454 break ;
5555 }
5656
57- layout->undoStack ()->beginMacro ( QObject::tr ( " Aligned items bottom " ) );
57+ layout->undoStack ()->beginMacro ( undoText ( alignment ) );
5858 for ( QgsLayoutItem *item : items )
5959 {
6060 layout->undoStack ()->beginCommand ( item, QString () );
6161
6262 QPointF shifted = item->pos ();
6363 switch ( alignment )
6464 {
65- case Left :
65+ case AlignLeft :
6666 shifted.setX ( refCoord );
6767 break ;
68- case HCenter :
68+ case AlignHCenter :
6969 shifted.setX ( refCoord - item->rect ().width () / 2.0 );
7070 break ;
71- case Right :
71+ case AlignRight :
7272 shifted.setX ( refCoord - item->rect ().width () );
7373 break ;
74- case Top :
74+ case AlignTop :
7575 shifted.setY ( refCoord );
7676 break ;
77- case VCenter :
77+ case AlignVCenter :
7878 shifted.setY ( refCoord - item->rect ().height () / 2.0 );
7979 break ;
80- case Bottom :
80+ case AlignBottom :
8181 shifted.setY ( refCoord - item->rect ().height () );
8282 break ;
8383 }
@@ -91,6 +91,91 @@ void QgsLayoutAligner::alignItems( QgsLayout *layout, const QList<QgsLayoutItem
9191 layout->undoStack ()->endMacro ();
9292}
9393
94+ void QgsLayoutAligner::distributeItems ( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Distribution distribution )
95+ {
96+ if ( items.size () < 2 )
97+ return ;
98+
99+ auto collectReferenceCoord = [distribution]( QgsLayoutItem * item )->double
100+ {
101+ QRectF itemBBox = item->sceneBoundingRect ();
102+ switch ( distribution )
103+ {
104+ case AlignLeft:
105+ return itemBBox.left ();
106+ case AlignHCenter:
107+ return itemBBox.center ().x ();
108+ case AlignRight:
109+ return itemBBox.right ();
110+ case AlignTop:
111+ return itemBBox.top ();
112+ case AlignVCenter:
113+ return itemBBox.center ().y ();
114+ case AlignBottom:
115+ return itemBBox.bottom ();
116+ }
117+ // no warnings
118+ return itemBBox.left ();
119+ };
120+
121+
122+ double minCoord = DBL_MAX;
123+ double maxCoord = -DBL_MAX;
124+ QMap< double , QgsLayoutItem * > itemCoords;
125+ for ( QgsLayoutItem *item : items )
126+ {
127+ double refCoord = collectReferenceCoord ( item );
128+ minCoord = std::min ( minCoord, refCoord );
129+ maxCoord = std::max ( maxCoord, refCoord );
130+ itemCoords.insert ( refCoord, item );
131+ }
132+
133+ double step = ( maxCoord - minCoord ) / ( items.size () - 1 );
134+
135+ auto distributeItemToCoord = [layout, distribution]( QgsLayoutItem * item, double refCoord )
136+ {
137+ QPointF shifted = item->pos ();
138+ switch ( distribution )
139+ {
140+ case DistributeLeft:
141+ shifted.setX ( refCoord );
142+ break ;
143+ case DistributeHCenter:
144+ shifted.setX ( refCoord - item->rect ().width () / 2.0 );
145+ break ;
146+ case DistributeRight:
147+ shifted.setX ( refCoord - item->rect ().width () );
148+ break ;
149+ case DistributeTop:
150+ shifted.setY ( refCoord );
151+ break ;
152+ case DistributeVCenter:
153+ shifted.setY ( refCoord - item->rect ().height () / 2.0 );
154+ break ;
155+ case DistributeBottom:
156+ shifted.setY ( refCoord - item->rect ().height () );
157+ break ;
158+ }
159+
160+ // need to keep item units
161+ QgsLayoutPoint newPos = layout->convertFromLayoutUnits ( shifted, item->positionWithUnits ().units () );
162+ item->attemptMove ( newPos );
163+ };
164+
165+
166+ layout->undoStack ()->beginMacro ( undoText ( distribution ) );
167+ double currentVal = minCoord;
168+ for ( auto itemIt = itemCoords.constBegin (); itemIt != itemCoords.constEnd (); ++itemIt )
169+ {
170+ layout->undoStack ()->beginCommand ( itemIt.value (), QString () );
171+ distributeItemToCoord ( itemIt.value (), currentVal );
172+ layout->undoStack ()->endCommand ();
173+
174+ currentVal += step;
175+ }
176+ layout->undoStack ()->endMacro ();
177+ }
178+
94179QRectF QgsLayoutAligner::boundingRectOfItems ( const QList<QgsLayoutItem *> &items )
95180{
96181 if ( items.empty () )
@@ -129,3 +214,43 @@ QRectF QgsLayoutAligner::boundingRectOfItems( const QList<QgsLayoutItem *> &item
129214
130215 return QRectF ( QPointF ( minX, minY ), QPointF ( maxX, maxY ) );
131216}
217+
218+ QString QgsLayoutAligner::undoText ( Distribution distribution )
219+ {
220+ switch ( distribution )
221+ {
222+ case DistributeLeft:
223+ return QObject::tr ( " Distributed items by left" );
224+ case DistributeHCenter:
225+ return QObject::tr ( " Distributed items by center" );
226+ case DistributeRight:
227+ return QObject::tr ( " Distributed items by right" );
228+ case DistributeTop:
229+ return QObject::tr ( " Distributed items by top" );
230+ case DistributeVCenter:
231+ return QObject::tr ( " Distributed items by vertical center" );
232+ case DistributeBottom:
233+ return QObject::tr ( " Distributed items by bottom" );
234+ }
235+ return QString (); // no warnings
236+ }
237+
238+ QString QgsLayoutAligner::undoText ( Alignment alignment )
239+ {
240+ switch ( alignment )
241+ {
242+ case AlignLeft:
243+ return QObject::tr ( " Aligned items to left" );
244+ case AlignHCenter:
245+ return QObject::tr ( " Aligned items to center" );
246+ case AlignRight:
247+ return QObject::tr ( " Aligned items to right" );
248+ case AlignTop:
249+ return QObject::tr ( " Aligned items to top" );
250+ case AlignVCenter:
251+ return QObject::tr ( " Aligned items to vertical center" );
252+ case AlignBottom:
253+ return QObject::tr ( " Aligned items to bottom" );
254+ }
255+ return QString (); // no warnings
256+ }
0 commit comments