@@ -44,7 +44,12 @@ void QgsLayoutSnapper::setSnapToGuides( bool enabled )
44
44
mSnapToGuides = enabled;
45
45
}
46
46
47
- QPointF QgsLayoutSnapper::snapPoint ( QPointF point, double scaleFactor, bool &snapped ) const
47
+ void QgsLayoutSnapper::setSnapToItems ( bool enabled )
48
+ {
49
+ mSnapToItems = enabled;
50
+ }
51
+
52
+ QPointF QgsLayoutSnapper::snapPoint ( QPointF point, double scaleFactor, bool &snapped, QGraphicsLineItem *horizontalSnapLine, QGraphicsLineItem *verticalSnapLine ) const
48
53
{
49
54
snapped = false ;
50
55
@@ -55,24 +60,49 @@ QPointF QgsLayoutSnapper::snapPoint( QPointF point, double scaleFactor, bool &sn
55
60
{
56
61
snapped = true ;
57
62
point.setX ( newX );
63
+ if ( verticalSnapLine )
64
+ verticalSnapLine->setVisible ( false );
58
65
}
59
66
bool snappedYToGuides = false ;
60
67
double newY = snapPointToGuides ( point.y (), QgsLayoutGuide::Horizontal, scaleFactor, snappedYToGuides );
61
68
if ( snappedYToGuides )
62
69
{
63
70
snapped = true ;
64
71
point.setY ( newY );
72
+ if ( horizontalSnapLine )
73
+ horizontalSnapLine->setVisible ( false );
74
+ }
75
+
76
+ bool snappedXToItems = false ;
77
+ bool snappedYToItems = false ;
78
+ if ( !snappedXToGuides )
79
+ {
80
+ newX = snapPointToItems ( point.x (), Qt::Horizontal, scaleFactor, QList< QgsLayoutItem * >(), snappedXToItems, verticalSnapLine );
81
+ if ( snappedXToItems )
82
+ {
83
+ snapped = true ;
84
+ point.setX ( newX );
85
+ }
86
+ }
87
+ if ( !snappedYToGuides )
88
+ {
89
+ newY = snapPointToItems ( point.y (), Qt::Vertical, scaleFactor, QList< QgsLayoutItem * >(), snappedYToItems, horizontalSnapLine );
90
+ if ( snappedYToItems )
91
+ {
92
+ snapped = true ;
93
+ point.setY ( newY );
94
+ }
65
95
}
66
96
67
97
bool snappedXToGrid = false ;
68
98
bool snappedYToGrid = false ;
69
99
QPointF res = snapPointToGrid ( point, scaleFactor, snappedXToGrid, snappedYToGrid );
70
- if ( snappedXToGrid && !snappedXToGuides )
100
+ if ( snappedXToGrid && !snappedXToGuides && !snappedXToItems )
71
101
{
72
102
snapped = true ;
73
103
point.setX ( res.x () );
74
104
}
75
- if ( snappedYToGrid && !snappedYToGuides )
105
+ if ( snappedYToGrid && !snappedYToGuides && !snappedYToItems )
76
106
{
77
107
snapped = true ;
78
108
point.setY ( res.y () );
@@ -169,13 +199,117 @@ double QgsLayoutSnapper::snapPointToGuides( double original, QgsLayoutGuide::Ori
169
199
}
170
200
}
171
201
202
+ double QgsLayoutSnapper::snapPointToItems ( double original, Qt::Orientation orientation, double scaleFactor, const QList<QgsLayoutItem *> &ignoreItems, bool &snapped,
203
+ QGraphicsLineItem *snapLine ) const
204
+ {
205
+ snapped = false ;
206
+ if ( !mLayout || !mSnapToItems )
207
+ {
208
+ if ( snapLine )
209
+ snapLine->setVisible ( false );
210
+ return original;
211
+ }
212
+
213
+ double alignThreshold = mTolerance / scaleFactor;
214
+
215
+ double closest = original;
216
+ double closestDist = DBL_MAX;
217
+ const QList<QGraphicsItem *> itemList = mLayout ->items ();
218
+ QList< double > currentCoords;
219
+ for ( QGraphicsItem *item : itemList )
220
+ {
221
+ QgsLayoutItem *currentItem = dynamic_cast < QgsLayoutItem *>( item );
222
+ if ( ignoreItems.contains ( currentItem ) )
223
+ continue ;
224
+
225
+ // don't snap to selected items, since they're the ones that will be snapping to something else
226
+ // also ignore group members - only snap to bounds of group itself
227
+ // also ignore hidden items
228
+ if ( !currentItem /* TODO || currentItem->selected() || currentItem->isGroupMember() */ || !currentItem->isVisible () )
229
+ {
230
+ continue ;
231
+ }
232
+ QRectF itemRect;
233
+ if ( dynamic_cast <const QgsLayoutItemPage *>( currentItem ) )
234
+ {
235
+ // if snapping to paper use the paper item's rect rather then the bounding rect,
236
+ // since we want to snap to the page edge and not any outlines drawn around the page
237
+ itemRect = currentItem->mapRectToScene ( currentItem->rect () );
238
+ }
239
+ else
240
+ {
241
+ itemRect = currentItem->mapRectToScene ( currentItem->rectWithFrame () );
242
+ }
243
+
244
+ currentCoords.clear ();
245
+ switch ( orientation )
246
+ {
247
+ case Qt::Horizontal:
248
+ {
249
+ currentCoords << itemRect.left ();
250
+ currentCoords << itemRect.right ();
251
+ currentCoords << itemRect.center ().x ();
252
+ break ;
253
+ }
254
+
255
+ case Qt::Vertical:
256
+ {
257
+ currentCoords << itemRect.top ();
258
+ currentCoords << itemRect.center ().y ();
259
+ currentCoords << itemRect.bottom ();
260
+ break ;
261
+ }
262
+ }
263
+
264
+ for ( double val : qgsAsConst ( currentCoords ) )
265
+ {
266
+ double dist = std::fabs ( original - val );
267
+ if ( dist <= alignThreshold && dist < closestDist )
268
+ {
269
+ snapped = true ;
270
+ closestDist = dist;
271
+ closest = val;
272
+ }
273
+ }
274
+ }
275
+
276
+ if ( snapLine )
277
+ {
278
+ if ( snapped )
279
+ {
280
+ snapLine->setVisible ( true );
281
+ switch ( orientation )
282
+ {
283
+ case Qt::Vertical:
284
+ {
285
+ snapLine->setLine ( QLineF ( 0 , closest, 300 , closest ) );
286
+ break ;
287
+ }
288
+
289
+ case Qt::Horizontal:
290
+ {
291
+ snapLine->setLine ( QLineF ( closest, 0 , closest, 300 ) );
292
+ break ;
293
+ }
294
+ }
295
+ }
296
+ else
297
+ {
298
+ snapLine->setVisible ( false );
299
+ }
300
+ }
301
+
302
+ return closest;
303
+ }
304
+
172
305
bool QgsLayoutSnapper::writeXml ( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext & ) const
173
306
{
174
307
QDomElement element = document.createElement ( QStringLiteral ( " Snapper" ) );
175
308
176
309
element.setAttribute ( QStringLiteral ( " tolerance" ), mTolerance );
177
310
element.setAttribute ( QStringLiteral ( " snapToGrid" ), mSnapToGrid );
178
311
element.setAttribute ( QStringLiteral ( " snapToGuides" ), mSnapToGuides );
312
+ element.setAttribute ( QStringLiteral ( " snapToItems" ), mSnapToItems );
179
313
180
314
parentElement.appendChild ( element );
181
315
return true ;
@@ -197,7 +331,6 @@ bool QgsLayoutSnapper::readXml( const QDomElement &e, const QDomDocument &, cons
197
331
mTolerance = element.attribute ( QStringLiteral ( " tolerance" ), QStringLiteral ( " 5" ) ).toInt ();
198
332
mSnapToGrid = element.attribute ( QStringLiteral ( " snapToGrid" ), QStringLiteral ( " 0" ) ) != QLatin1String ( " 0" );
199
333
mSnapToGuides = element.attribute ( QStringLiteral ( " snapToGuides" ), QStringLiteral ( " 0" ) ) != QLatin1String ( " 0" );
334
+ mSnapToItems = element.attribute ( QStringLiteral ( " snapToItems" ), QStringLiteral ( " 0" ) ) != QLatin1String ( " 0" );
200
335
return true ;
201
336
}
202
-
203
-
0 commit comments