27using namespace Qt::StringLiterals;
48 mSnapToGrid = enabled;
53 mSnapToGuides = enabled;
58 mSnapToItems = enabled;
61QPointF
QgsLayoutSnapper::snapPoint( QPointF point,
double scaleFactor,
bool &snapped, QGraphicsLineItem *horizontalSnapLine, QGraphicsLineItem *verticalSnapLine,
62 const QList< QgsLayoutItem * > *ignoreItems )
const
67 bool snappedXToGuides =
false;
68 double newX =
snapPointToGuides( point.x(), Qt::Vertical, scaleFactor, snappedXToGuides );
69 if ( snappedXToGuides )
73 if ( verticalSnapLine )
74 verticalSnapLine->setVisible(
false );
76 bool snappedYToGuides =
false;
77 double newY =
snapPointToGuides( point.y(), Qt::Horizontal, scaleFactor, snappedYToGuides );
78 if ( snappedYToGuides )
82 if ( horizontalSnapLine )
83 horizontalSnapLine->setVisible(
false );
86 bool snappedXToItems =
false;
87 bool snappedYToItems =
false;
88 if ( !snappedXToGuides )
90 newX =
snapPointToItems( point.x(), Qt::Horizontal, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedXToItems, verticalSnapLine );
91 if ( snappedXToItems )
97 if ( !snappedYToGuides )
99 newY =
snapPointToItems( point.y(), Qt::Vertical, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedYToItems, horizontalSnapLine );
100 if ( snappedYToItems )
107 bool snappedXToGrid =
false;
108 bool snappedYToGrid =
false;
109 QPointF res =
snapPointToGrid( point, scaleFactor, snappedXToGrid, snappedYToGrid );
110 if ( snappedXToGrid && !snappedXToGuides && !snappedXToItems )
113 point.setX( res.x() );
115 if ( snappedYToGrid && !snappedYToGuides && !snappedYToItems )
118 point.setY( res.y() );
124QRectF
QgsLayoutSnapper::snapRect(
const QRectF &rect,
double scaleFactor,
bool &snapped, QGraphicsLineItem *horizontalSnapLine, QGraphicsLineItem *verticalSnapLine,
const QList<QgsLayoutItem *> *ignoreItems )
const
127 QRectF snappedRect = rect;
129 QList< double > xCoords;
130 xCoords << rect.left() << rect.center().x() << rect.right();
131 QList< double > yCoords;
132 yCoords << rect.top() << rect.center().y() << rect.bottom();
135 bool snappedXToGuides =
false;
136 double deltaX =
snapPointsToGuides( xCoords, Qt::Vertical, scaleFactor, snappedXToGuides );
137 if ( snappedXToGuides )
140 snappedRect.translate( deltaX, 0 );
141 if ( verticalSnapLine )
142 verticalSnapLine->setVisible(
false );
144 bool snappedYToGuides =
false;
145 double deltaY =
snapPointsToGuides( yCoords, Qt::Horizontal, scaleFactor, snappedYToGuides );
146 if ( snappedYToGuides )
149 snappedRect.translate( 0, deltaY );
150 if ( horizontalSnapLine )
151 horizontalSnapLine->setVisible(
false );
154 bool snappedXToItems =
false;
155 bool snappedYToItems =
false;
156 if ( !snappedXToGuides )
158 deltaX =
snapPointsToItems( xCoords, Qt::Horizontal, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedXToItems, verticalSnapLine );
159 if ( snappedXToItems )
162 snappedRect.translate( deltaX, 0 );
165 if ( !snappedYToGuides )
167 deltaY =
snapPointsToItems( yCoords, Qt::Vertical, scaleFactor, ignoreItems ? *ignoreItems : QList< QgsLayoutItem * >(), snappedYToItems, horizontalSnapLine );
168 if ( snappedYToItems )
171 snappedRect.translate( 0, deltaY );
175 bool snappedXToGrid =
false;
176 bool snappedYToGrid =
false;
177 QList< QPointF > points;
178 points << rect.topLeft() << rect.topRight() << rect.bottomLeft() << rect.bottomRight();
179 QPointF res =
snapPointsToGrid( points, scaleFactor, snappedXToGrid, snappedYToGrid );
180 if ( snappedXToGrid && !snappedXToGuides && !snappedXToItems )
183 snappedRect.translate( res.x(), 0 );
185 if ( snappedYToGrid && !snappedYToGuides && !snappedYToItems )
188 snappedRect.translate( 0, res.y() );
196 QPointF delta =
snapPointsToGrid( QList< QPointF >() << point, scaleFactor, snappedX, snappedY );
197 return point + delta;
204 if ( !mLayout || !mSnapToGrid )
206 return QPointF( 0, 0 );
210 return QPointF( 0, 0 );
214 double smallestDiffX = std::numeric_limits<double>::max();
215 double smallestDiffY = std::numeric_limits<double>::max();
216 for ( QPointF point : points )
219 QPointF pagePoint = mLayout->pageCollection()->positionOnPage( point );
221 double yPage = pagePoint.y();
222 double yAtTopOfPage = mLayout->pageCollection()->page( mLayout->pageCollection()->pageNumberForPoint( point ) )->pos().y();
225 double gridRes = mLayout->convertToLayoutUnits( grid.
resolution() );
226 QPointF gridOffset = mLayout->convertToLayoutUnits( grid.
offset() );
227 int xRatio =
static_cast< int >( ( point.x() - gridOffset.x() ) / gridRes + 0.5 );
228 int yRatio =
static_cast< int >( ( yPage - gridOffset.y() ) / gridRes + 0.5 );
230 double xSnapped = xRatio * gridRes + gridOffset.x();
231 double ySnapped = yRatio * gridRes + gridOffset.y() + yAtTopOfPage;
233 double currentDiffX = std::fabs( xSnapped - point.x() );
234 if ( currentDiffX < smallestDiffX )
236 smallestDiffX = currentDiffX;
237 deltaX = xSnapped - point.x();
240 double currentDiffY = std::fabs( ySnapped - point.y() );
241 if ( currentDiffY < smallestDiffY )
243 smallestDiffY = currentDiffY;
244 deltaY = ySnapped - point.y();
249 double alignThreshold = mTolerance / scaleFactor;
251 QPointF delta( 0, 0 );
252 if ( smallestDiffX <= alignThreshold )
256 delta.setX( deltaX );
258 if ( smallestDiffY <= alignThreshold )
262 delta.setY( deltaY );
270 double delta =
snapPointsToGuides( QList< double >() << original, orientation, scaleFactor, snapped );
271 return original + delta;
277 if ( !mLayout || !mSnapToGuides )
283 double alignThreshold = mTolerance / scaleFactor;
285 double bestDelta = 0;
286 double smallestDiff = std::numeric_limits<double>::max();
288 for (
double p : points )
290 const auto constGuides = mLayout->guides().guides( orientation );
293 double guidePos = guide->layoutPosition();
294 double diff = std::fabs( p - guidePos );
295 if ( diff < smallestDiff )
298 bestDelta = guidePos - p;
303 if ( smallestDiff <= alignThreshold )
315 QGraphicsLineItem *snapLine )
const
317 double delta =
snapPointsToItems( QList< double >() << original, orientation, scaleFactor, ignoreItems, snapped, snapLine );
318 return original + delta;
321double QgsLayoutSnapper::snapPointsToItems(
const QList<double> &points, Qt::Orientation orientation,
double scaleFactor,
const QList<QgsLayoutItem *> &ignoreItems,
bool &snapped, QGraphicsLineItem *snapLine )
const
324 if ( !mLayout || !mSnapToItems )
327 snapLine->setVisible(
false );
331 double alignThreshold = mTolerance / scaleFactor;
333 double bestDelta = 0;
334 double smallestDiff = std::numeric_limits<double>::max();
336 const QList<QGraphicsItem *> itemList = mLayout->items();
337 QList< double > currentCoords;
338 for ( QGraphicsItem *item : itemList )
341 if ( !currentItem || ignoreItems.contains( currentItem ) )
345 if ( !currentItem->isVisible() )
353 itemRect = currentItem->mapRectToScene( currentItem->rect() );
357 itemRect = currentItem->mapRectToScene( currentItem->
rectWithFrame() );
360 currentCoords.clear();
361 switch ( orientation )
365 currentCoords << itemRect.left();
366 currentCoords << itemRect.right();
367 currentCoords << itemRect.center().x();
373 currentCoords << itemRect.top();
374 currentCoords << itemRect.center().y();
375 currentCoords << itemRect.bottom();
380 for (
double val : std::as_const( currentCoords ) )
382 for (
double p : points )
384 double dist = std::fabs( p - val );
385 if ( dist <= alignThreshold && dist < smallestDiff )
400 snapLine->setVisible(
true );
401 switch ( orientation )
405 snapLine->setLine( QLineF( -100000, closest, 100000, closest ) );
411 snapLine->setLine( QLineF( closest, -100000, closest, 100000 ) );
418 snapLine->setVisible(
false );
428 QDomElement element = document.createElement( u
"Snapper"_s );
430 element.setAttribute( u
"tolerance"_s, mTolerance );
431 element.setAttribute( u
"snapToGrid"_s, mSnapToGrid );
432 element.setAttribute( u
"snapToGuides"_s, mSnapToGuides );
433 element.setAttribute( u
"snapToItems"_s, mSnapToItems );
435 parentElement.appendChild( element );
441 QDomElement element = e;
442 if ( element.nodeName() !=
"Snapper"_L1 )
444 element = element.firstChildElement( u
"Snapper"_s );
447 if ( element.nodeName() !=
"Snapper"_L1 )
452 mTolerance = element.attribute( u
"tolerance"_s, u
"5"_s ).toInt();
453 mSnapToGrid = element.attribute( u
"snapToGrid"_s, u
"0"_s ) !=
"0"_L1;
454 mSnapToGuides = element.attribute( u
"snapToGuides"_s, u
"0"_s ) !=
"0"_L1;
455 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.
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.
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.