28using namespace Qt::StringLiterals;
51 mSnapToGrid = enabled;
56 mSnapToGuides = enabled;
61 mSnapToItems = enabled;
65 QPointF point,
double scaleFactor,
bool &snapped, QGraphicsLineItem *horizontalSnapLine, QGraphicsLineItem *verticalSnapLine,
const QList< QgsLayoutItem * > *ignoreItems
71 bool snappedXToGuides =
false;
72 double newX =
snapPointToGuides( point.x(), Qt::Vertical, scaleFactor, snappedXToGuides );
73 if ( snappedXToGuides )
77 if ( verticalSnapLine )
78 verticalSnapLine->setVisible(
false );
80 bool snappedYToGuides =
false;
81 double newY =
snapPointToGuides( point.y(), Qt::Horizontal, scaleFactor, snappedYToGuides );
82 if ( snappedYToGuides )
86 if ( horizontalSnapLine )
87 horizontalSnapLine->setVisible(
false );
90 bool snappedXToItems =
false;
91 bool snappedYToItems =
false;
92 if ( !snappedXToGuides )
94 newX =
snapPointToItems( point.x(), Qt::Horizontal, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedXToItems, verticalSnapLine );
95 if ( snappedXToItems )
101 if ( !snappedYToGuides )
103 newY =
snapPointToItems( point.y(), Qt::Vertical, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedYToItems, horizontalSnapLine );
104 if ( snappedYToItems )
111 bool snappedXToGrid =
false;
112 bool snappedYToGrid =
false;
113 QPointF res =
snapPointToGrid( point, scaleFactor, snappedXToGrid, snappedYToGrid );
114 if ( snappedXToGrid && !snappedXToGuides && !snappedXToItems )
117 point.setX( res.x() );
119 if ( snappedYToGrid && !snappedYToGuides && !snappedYToItems )
122 point.setY( res.y() );
129 const QRectF &rect,
double scaleFactor,
bool &snapped, QGraphicsLineItem *horizontalSnapLine, QGraphicsLineItem *verticalSnapLine,
const QList<QgsLayoutItem *> *ignoreItems
133 QRectF snappedRect = rect;
135 QList< double > xCoords;
136 xCoords << rect.left() << rect.center().x() << rect.right();
137 QList< double > yCoords;
138 yCoords << rect.top() << rect.center().y() << rect.bottom();
141 bool snappedXToGuides =
false;
142 double deltaX =
snapPointsToGuides( xCoords, Qt::Vertical, scaleFactor, snappedXToGuides );
143 if ( snappedXToGuides )
146 snappedRect.translate( deltaX, 0 );
147 if ( verticalSnapLine )
148 verticalSnapLine->setVisible(
false );
150 bool snappedYToGuides =
false;
151 double deltaY =
snapPointsToGuides( yCoords, Qt::Horizontal, scaleFactor, snappedYToGuides );
152 if ( snappedYToGuides )
155 snappedRect.translate( 0, deltaY );
156 if ( horizontalSnapLine )
157 horizontalSnapLine->setVisible(
false );
160 bool snappedXToItems =
false;
161 bool snappedYToItems =
false;
162 if ( !snappedXToGuides )
164 deltaX =
snapPointsToItems( xCoords, Qt::Horizontal, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedXToItems, verticalSnapLine );
165 if ( snappedXToItems )
168 snappedRect.translate( deltaX, 0 );
171 if ( !snappedYToGuides )
173 deltaY =
snapPointsToItems( yCoords, Qt::Vertical, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedYToItems, horizontalSnapLine );
174 if ( snappedYToItems )
177 snappedRect.translate( 0, deltaY );
181 bool snappedXToGrid =
false;
182 bool snappedYToGrid =
false;
183 QList< QPointF > points;
184 points << rect.topLeft() << rect.topRight() << rect.bottomLeft() << rect.bottomRight();
185 QPointF res =
snapPointsToGrid( points, scaleFactor, snappedXToGrid, snappedYToGrid );
186 if ( snappedXToGrid && !snappedXToGuides && !snappedXToItems )
189 snappedRect.translate( res.x(), 0 );
191 if ( snappedYToGrid && !snappedYToGuides && !snappedYToItems )
194 snappedRect.translate( 0, res.y() );
202 QPointF delta =
snapPointsToGrid( QList< QPointF >() << point, scaleFactor, snappedX, snappedY );
203 return point + delta;
210 if ( !mLayout || !mSnapToGrid )
212 return QPointF( 0, 0 );
216 return QPointF( 0, 0 );
220 double smallestDiffX = std::numeric_limits<double>::max();
221 double smallestDiffY = std::numeric_limits<double>::max();
222 for ( QPointF point : points )
225 QPointF pagePoint = mLayout->pageCollection()->positionOnPage( point );
227 double yPage = pagePoint.y();
228 double yAtTopOfPage = mLayout->pageCollection()->page( mLayout->pageCollection()->pageNumberForPoint( point ) )->pos().y();
231 double gridRes = mLayout->convertToLayoutUnits( grid.
resolution() );
232 QPointF gridOffset = mLayout->convertToLayoutUnits( grid.
offset() );
233 int xRatio =
static_cast< int >( ( point.x() - gridOffset.x() ) / gridRes + 0.5 );
234 int yRatio =
static_cast< int >( ( yPage - gridOffset.y() ) / gridRes + 0.5 );
236 double xSnapped = xRatio * gridRes + gridOffset.x();
237 double ySnapped = yRatio * gridRes + gridOffset.y() + yAtTopOfPage;
239 double currentDiffX = std::fabs( xSnapped - point.x() );
240 if ( currentDiffX < smallestDiffX )
242 smallestDiffX = currentDiffX;
243 deltaX = xSnapped - point.x();
246 double currentDiffY = std::fabs( ySnapped - point.y() );
247 if ( currentDiffY < smallestDiffY )
249 smallestDiffY = currentDiffY;
250 deltaY = ySnapped - point.y();
255 double alignThreshold = mTolerance / scaleFactor;
257 QPointF delta( 0, 0 );
258 if ( smallestDiffX <= alignThreshold )
262 delta.setX( deltaX );
264 if ( smallestDiffY <= alignThreshold )
268 delta.setY( deltaY );
276 double delta =
snapPointsToGuides( QList< double >() << original, orientation, scaleFactor, snapped );
277 return original + delta;
283 if ( !mLayout || !mSnapToGuides )
289 double alignThreshold = mTolerance / scaleFactor;
291 double bestDelta = 0;
292 double smallestDiff = std::numeric_limits<double>::max();
294 for (
double p : points )
296 const auto constGuides = mLayout->guides().guides( orientation );
299 double guidePos = guide->layoutPosition();
300 double diff = std::fabs( p - guidePos );
301 if ( diff < smallestDiff )
304 bestDelta = guidePos - p;
309 if ( smallestDiff <= alignThreshold )
320double QgsLayoutSnapper::snapPointToItems(
double original, Qt::Orientation orientation,
double scaleFactor,
const QList<QgsLayoutItem *> &ignoreItems,
bool &snapped, QGraphicsLineItem *snapLine )
const
322 double delta =
snapPointsToItems( QList< double >() << original, orientation, scaleFactor, ignoreItems, snapped, snapLine );
323 return original + delta;
327 const QList<double> &points, Qt::Orientation orientation,
double scaleFactor,
const QList<QgsLayoutItem *> &ignoreItems,
bool &snapped, QGraphicsLineItem *snapLine
331 if ( !mLayout || !mSnapToItems )
334 snapLine->setVisible(
false );
338 double alignThreshold = mTolerance / scaleFactor;
340 double bestDelta = 0;
341 double smallestDiff = std::numeric_limits<double>::max();
343 const QList<QGraphicsItem *> itemList = mLayout->items();
344 QList< double > currentCoords;
345 for ( QGraphicsItem *item : itemList )
348 if ( !currentItem || ignoreItems.contains( currentItem ) )
352 if ( !currentItem->isVisible() )
360 itemRect = currentItem->mapRectToScene( currentItem->rect() );
364 itemRect = currentItem->mapRectToScene( currentItem->
rectWithFrame() );
367 currentCoords.clear();
368 switch ( orientation )
372 currentCoords << itemRect.left();
373 currentCoords << itemRect.right();
374 currentCoords << itemRect.center().x();
380 currentCoords << itemRect.top();
381 currentCoords << itemRect.center().y();
382 currentCoords << itemRect.bottom();
387 for (
double val : std::as_const( currentCoords ) )
389 for (
double p : points )
391 double dist = std::fabs( p - val );
392 if ( dist <= alignThreshold && dist < smallestDiff )
407 snapLine->setVisible(
true );
408 switch ( orientation )
412 snapLine->setLine( QLineF( -100000, closest, 100000, closest ) );
418 snapLine->setLine( QLineF( closest, -100000, closest, 100000 ) );
425 snapLine->setVisible(
false );
435 QDomElement element = document.createElement( u
"Snapper"_s );
437 element.setAttribute( u
"tolerance"_s, mTolerance );
438 element.setAttribute( u
"snapToGrid"_s, mSnapToGrid );
439 element.setAttribute( u
"snapToGuides"_s, mSnapToGuides );
440 element.setAttribute( u
"snapToItems"_s, mSnapToItems );
442 parentElement.appendChild( element );
448 QDomElement element = e;
449 if ( element.nodeName() !=
"Snapper"_L1 )
451 element = element.firstChildElement( u
"Snapper"_s );
454 if ( element.nodeName() !=
"Snapper"_L1 )
459 mTolerance = element.attribute( u
"tolerance"_s, u
"5"_s ).toInt();
460 mSnapToGrid = element.attribute( u
"snapToGrid"_s, u
"0"_s ) !=
"0"_L1;
461 mSnapToGuides = element.attribute( u
"snapToGuides"_s, u
"0"_s ) !=
"0"_L1;
462 mSnapToItems = element.attribute( u
"snapToItems"_s, u
"0"_s ) !=
"0"_L1;
Contains settings relating to the appearance, spacing and offset for layout grids.
QgsLayoutMeasurement resolution() const
Returns the page/snap grid resolution.
QgsLayoutPoint offset() const
Returns the offset of the page/snap grid.
Contains the configuration for a single snap guide used by a layout.
Item representing the paper in a layout.
@ LayoutGroup
Grouped item.
Base class for graphical items within a QgsLayout.
virtual QRectF rectWithFrame() const
Returns the item's rectangular bounds, including any bleed caused by the item's frame.
int type() const override
Returns a unique graphics item type identifier.
double length() const
Returns the length of the measurement.
QPointF snapPoint(QPointF point, double scaleFactor, bool &snapped, QGraphicsLineItem *horizontalSnapLine=nullptr, QGraphicsLineItem *verticalSnapLine=nullptr, const QList< QgsLayoutItem * > *ignoreItems=nullptr) const
Snaps a layout coordinate point.
void setSnapToItems(bool enabled)
Sets whether snapping to items is enabled.
QPointF snapPointsToGrid(const QList< QPointF > &points, double scaleFactor, bool &snappedX, bool &snappedY) const
Snaps a set of points to the grid.
QRectF snapRect(const QRectF &rect, double scaleFactor, bool &snapped, QGraphicsLineItem *horizontalSnapLine=nullptr, QGraphicsLineItem *verticalSnapLine=nullptr, const QList< QgsLayoutItem * > *ignoreItems=nullptr) const
Snaps a layout coordinate rect.
int snapTolerance() const
Returns the snap tolerance (in pixels) to use when snapping.
double snapPointsToItems(const QList< double > &points, Qt::Orientation orientation, double scaleFactor, const QList< QgsLayoutItem * > &ignoreItems, bool &snapped, QGraphicsLineItem *snapLine=nullptr) const
Snaps a set of points to the item bounds.
bool readXml(const QDomElement &gridElement, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets the snapper's state from a DOM element.
void setSnapToGuides(bool enabled)
Sets whether snapping to guides is enabled.
QgsLayoutSnapper(QgsLayout *layout)
Constructor for QgsLayoutSnapper, attached to the specified layout.
void setSnapTolerance(int snapTolerance)
Sets the snap tolerance (in pixels) to use when snapping.
double snapPointsToGuides(const QList< double > &points, Qt::Orientation orientation, double scaleFactor, bool &snapped) const
Snaps a set of points to the guides.
void setSnapToGrid(bool enabled)
Sets whether snapping to grid is enabled.
QgsLayout * layout() override
Returns the layout the object belongs to.
double snapPointToItems(double original, Qt::Orientation orientation, double scaleFactor, const QList< QgsLayoutItem * > &ignoreItems, bool &snapped, QGraphicsLineItem *snapLine=nullptr) const
Snaps an original layout coordinate to the item bounds.
static const QgsSettingsEntryInteger * settingsSnapTolerance
double snapPointToGuides(double original, Qt::Orientation orientation, double scaleFactor, bool &snapped) const
Snaps an original layout coordinate to the guides.
QPointF snapPointToGrid(QPointF point, double scaleFactor, bool &snappedX, bool &snappedY) const
Snaps a layout coordinate point to the grid.
bool writeXml(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores the snapper's state in a DOM element.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
A container for the context for various read/write operations on objects.
An integer settings entry.
static QgsSettingsTreeNode * sTreeLayout