21 #include <QDragEnterEvent>
22 #include <QGraphicsLineItem>
30 const int QgsLayoutRuler::VALID_SCALE_MULTIPLES[] = {1, 2, 5};
31 const int QgsLayoutRuler::VALID_SCALE_MAGNITUDES[] = {1, 10, 100, 1000, 10000};
35 , mOrientation( orientation )
37 setMouseTracking(
true );
41 mRulerFontMetrics.reset(
new QFontMetrics( mRulerFont ) );
46 mScaleMinPixelsWidth = mRulerFontMetrics->boundingRect( QStringLiteral(
"000" ) ).width() * 2.5;
48 mRulerMinSize = mRulerFontMetrics->height() * 1.5;
50 mMinPixelsPerDivision = mRulerMinSize / 4;
52 if ( mMinPixelsPerDivision < 2 )
53 mMinPixelsPerDivision = 2;
55 mPixelsBetweenLineAndText = mRulerMinSize / 10;
56 mTextBaseline = mRulerMinSize / 1.667;
57 mMinSpacingVerticalLabels = mRulerMinSize / 5;
59 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
60 double guideMarkerSize = mRulerFontMetrics->width( QStringLiteral(
"*" ) );
62 double guideMarkerSize = mRulerFontMetrics->horizontalAdvance(
'*' );
64 mDragGuideTolerance = guideMarkerSize;
65 switch ( mOrientation )
68 mGuideMarker << QPoint( -guideMarkerSize / 2, mRulerMinSize - guideMarkerSize ) << QPoint( 0, mRulerMinSize ) <<
69 QPoint( guideMarkerSize / 2, mRulerMinSize - guideMarkerSize );
73 mGuideMarker << QPoint( mRulerMinSize - guideMarkerSize, -guideMarkerSize / 2 ) << QPoint( mRulerMinSize, 0 ) <<
74 QPoint( mRulerMinSize - guideMarkerSize, guideMarkerSize / 2 );
81 return QSize( mRulerMinSize, mRulerMinSize );
95 drawGuideMarkers( &p, layout );
97 QTransform t = mTransform.inverted();
98 p.setFont( mRulerFont );
100 QBrush brush = p.brush();
101 QColor color = brush.color();
102 color.setAlphaF( 0.7 );
103 brush.setColor( color );
107 color.setAlphaF( 0.7 );
108 pen.setColor( color );
114 int mmDisplay = optimumScale( mScaleMinPixelsWidth, magnitude, multiple );
117 int numSmallDivisions = optimumNumberDivisions( mmDisplay, multiple );
119 switch ( mOrientation )
129 double startX = t.map( QPointF( 0, 0 ) ).x();
130 double endX = t.map( QPointF( width(), 0 ) ).x();
133 double markerPos = ( std::floor( startX / mmDisplay ) + 1 ) * mmDisplay;
136 drawSmallDivisions( &p, markerPos, numSmallDivisions, -mmDisplay );
138 while ( markerPos <= endX )
140 double pixelCoord = mTransform.map( QPointF( markerPos, 0 ) ).x();
143 p.drawLine( pixelCoord, 0, pixelCoord, mRulerMinSize );
144 p.drawText( QPointF( pixelCoord + mPixelsBetweenLineAndText, mTextBaseline ), QString::number( markerPos ) );
147 drawSmallDivisions( &p, markerPos, numSmallDivisions, mmDisplay, endX );
149 markerPos += mmDisplay;
160 double startY = t.map( QPointF( 0, 0 ) ).y();
161 double endY = t.map( QPointF( 0, height() ) ).y();
167 double currentPageY = 0;
168 for (
int page = 0; page < layout->
pageCollection()->pageCount(); ++page )
170 if ( currentY < startY )
173 currentPageY = currentY;
178 if ( currentY > endY )
184 double beforePageCoord = -mmDisplay;
185 double firstPageY = mTransform.map( QPointF( 0, 0 ) ).y();
188 while ( beforePageCoord > startY )
190 double pixelCoord = mTransform.map( QPointF( 0, beforePageCoord ) ).y();
191 p.drawLine( 0, pixelCoord, mRulerMinSize, pixelCoord );
193 QString label = QString::number( beforePageCoord );
194 int labelSize = mRulerFontMetrics->boundingRect( label ).width();
197 if ( pixelCoord + labelSize + 8 < firstPageY )
199 drawRotatedText( &p, QPointF( mTextBaseline, pixelCoord + mMinSpacingVerticalLabels + labelSize ), label );
203 drawSmallDivisions( &p, beforePageCoord, numSmallDivisions, mmDisplay );
205 beforePageCoord -= mmDisplay;
209 drawSmallDivisions( &p, beforePageCoord + mmDisplay, numSmallDivisions, -mmDisplay, startY );
212 double nextPageStartPos = 0;
213 int nextPageStartPixel = 0;
215 for (
int i = startPage; i <= endPage; ++i )
217 double pageCoord = 0;
220 double totalCoord = currentPageY;
227 nextPageStartPixel = mTransform.map( QPointF( 0, nextPageStartPos ) ).y();
232 nextPageStartPos = 0;
233 nextPageStartPixel = 0;
236 while ( ( totalCoord < nextPageStartPos ) || ( ( nextPageStartPos == 0 ) && ( totalCoord <= endY ) ) )
238 double pixelCoord = mTransform.map( QPointF( 0, totalCoord ) ).y();
239 p.drawLine( 0, pixelCoord, mRulerMinSize, pixelCoord );
241 QString label = QString::number( pageCoord );
242 int labelSize = mRulerFontMetrics->boundingRect( label ).width();
245 if ( ( pixelCoord + labelSize + 8 < nextPageStartPixel )
246 || ( nextPageStartPixel == 0 ) )
248 drawRotatedText( &p, QPointF( mTextBaseline, pixelCoord + mMinSpacingVerticalLabels + labelSize ), label );
252 drawSmallDivisions( &p, totalCoord, numSmallDivisions, mmDisplay, nextPageStartPos );
254 pageCoord += mmDisplay;
255 totalCoord += mmDisplay;
268 void QgsLayoutRuler::drawMarkerPos( QPainter *painter )
271 painter->setPen( QColor( Qt::red ) );
272 switch ( mOrientation )
276 painter->drawLine( mMarkerPos.x(), 0, mMarkerPos.x(), mRulerMinSize );
281 painter->drawLine( 0, mMarkerPos.y(), mRulerMinSize, mMarkerPos.y() );
287 void QgsLayoutRuler::drawGuideMarkers( QPainter *p,
QgsLayout *layout )
289 QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
290 QList< QgsLayoutGuide * > guides = layout->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
292 p->setRenderHint( QPainter::Antialiasing,
true );
293 p->setPen( Qt::NoPen );
294 const auto constGuides = guides;
297 if ( visiblePages.contains( guide->page() ) )
299 if ( guide == mHoverGuide )
301 p->setBrush( QBrush( QColor( 255, 0, 0, 225 ) ) );
305 p->setBrush( QBrush( QColor( 255, 0, 0, 150 ) ) );
308 switch ( mOrientation )
311 point = QPointF( guide->layoutPosition(), 0 );
315 point = QPointF( 0, guide->layoutPosition() );
318 drawGuideAtPos( p, convertLayoutPointToLocal( point ) );
324 void QgsLayoutRuler::drawGuideAtPos( QPainter *painter, QPoint pos )
326 switch ( mOrientation )
330 painter->translate( pos.x(), 0 );
331 painter->drawPolygon( mGuideMarker );
332 painter->translate( -pos.x(), 0 );
337 painter->translate( 0, pos.y() );
338 painter->drawPolygon( mGuideMarker );
339 painter->translate( 0, -pos.y() );
345 void QgsLayoutRuler::createTemporaryGuideItem()
351 mGuideItem =
new QGraphicsLineItem();
354 QPen linePen( Qt::DotLine );
355 linePen.setColor( QColor( 255, 0, 0, 150 ) );
356 linePen.setWidthF( 0 );
357 mGuideItem->setPen( linePen );
362 QPointF QgsLayoutRuler::convertLocalPointToLayout( QPoint localPoint )
const
364 QPoint viewPoint = mView->mapFromGlobal( mapToGlobal( localPoint ) );
365 return mView->mapToScene( viewPoint );
368 QPoint QgsLayoutRuler::convertLayoutPointToLocal( QPointF layoutPoint )
const
370 QPoint viewPoint = mView->mapFromScene( layoutPoint );
371 return mapFromGlobal( mView->mapToGlobal( viewPoint ) );
374 QgsLayoutGuide *QgsLayoutRuler::guideAtPoint( QPoint localPoint )
const
379 QPointF layoutPoint = convertLocalPointToLayout( localPoint );
380 QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
381 QList< QgsLayoutGuide * > guides = mView->
currentLayout()->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
383 double minDelta = std::numeric_limits<double>::max();
384 const auto constGuides = guides;
387 if ( visiblePages.contains( guide->page() ) )
389 double currentDelta = 0;
390 switch ( mOrientation )
393 currentDelta = std::fabs( layoutPoint.x() - guide->layoutPosition() );
397 currentDelta = std::fabs( layoutPoint.y() - guide->layoutPosition() );
400 if ( currentDelta < minDelta )
402 minDelta = currentDelta;
403 closestGuide = guide;
408 if ( minDelta * mView->transform().m11() <= mDragGuideTolerance )
418 void QgsLayoutRuler::drawRotatedText( QPainter *painter, QPointF pos,
const QString &text )
421 painter->translate( pos.x(), pos.y() );
422 painter->rotate( 270 );
423 painter->drawText( 0, 0, text );
427 void QgsLayoutRuler::drawSmallDivisions( QPainter *painter,
double startPos,
int numDivisions,
double rulerScale,
double maxPos )
429 if ( numDivisions == 0 )
433 double smallMarkerPos = startPos;
434 double smallDivisionSpacing = rulerScale / numDivisions;
436 double pixelCoord = 0.0;
439 for (
int i = 0; i < numDivisions; ++i )
441 smallMarkerPos += smallDivisionSpacing;
443 if ( maxPos > 0 && smallMarkerPos > maxPos )
450 switch ( mOrientation )
454 pixelCoord = mTransform.map( QPointF( smallMarkerPos, 0 ) ).x();
459 pixelCoord = mTransform.map( QPointF( 0, smallMarkerPos ) ).y();
466 if ( ( numDivisions == 10 && i == 4 ) || ( numDivisions == 4 && i == 1 ) )
469 lineSize = mRulerMinSize / 1.5;
473 lineSize = mRulerMinSize / 1.25;
477 switch ( mOrientation )
481 painter->drawLine( pixelCoord, lineSize, pixelCoord, mRulerMinSize );
486 painter->drawLine( lineSize, pixelCoord, mRulerMinSize, pixelCoord );
493 int QgsLayoutRuler::optimumScale(
double minPixelDiff,
int &magnitude,
int &multiple )
498 for (
unsigned int magnitudeCandidate = 0; magnitudeCandidate <
COUNT_VALID_MAGNITUDES; ++magnitudeCandidate )
500 for (
unsigned int multipleCandidate = 0; multipleCandidate <
COUNT_VALID_MULTIPLES; ++multipleCandidate )
502 int candidateScale = VALID_SCALE_MULTIPLES[multipleCandidate] * VALID_SCALE_MAGNITUDES[magnitudeCandidate];
504 double pixelDiff = mTransform.map( QPointF( candidateScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
505 if ( pixelDiff > minPixelDiff )
508 magnitude = VALID_SCALE_MAGNITUDES[magnitudeCandidate];
509 multiple = VALID_SCALE_MULTIPLES[multipleCandidate];
510 return candidateScale;
518 int QgsLayoutRuler::optimumNumberDivisions(
double rulerScale,
int scaleMultiple )
521 double largeDivisionSize = mTransform.map( QPointF( rulerScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
524 QList<int> validSmallDivisions;
525 switch ( scaleMultiple )
530 validSmallDivisions << 10 << 5 << 2;
535 validSmallDivisions << 10 << 4 << 2;
540 validSmallDivisions << 10 << 5;
545 QList<int>::iterator divisions_it;
546 for ( divisions_it = validSmallDivisions.begin(); divisions_it != validSmallDivisions.end(); ++divisions_it )
549 double candidateSize = largeDivisionSize / ( *divisions_it );
551 if ( candidateSize >= mMinPixelsPerDivision )
554 return ( *divisions_it );
565 mTransform = transform;
582 mMarkerPos = mView->mapFromScene( position );
588 mMarkerPos =
event->pos();
595 if ( mCreatingGuide || mDraggingGuide )
598 displayPos = convertLocalPointToLayout( event->pos() );
600 if ( mCreatingGuide )
608 QPen linePen = mGuideItem->pen();
612 linePen.setColor( QColor( 255, 0, 0, 150 ) );
616 linePen.setColor( QColor( 255, 0, 0, 225 ) );
618 mGuideItem->setPen( linePen );
619 switch ( mOrientation )
624 mGuideItem->setLine( page->scenePos().x(), displayPos.y(), page->scenePos().x() + page->rect().width(), displayPos.y() );
625 displayPos.setX( 0 );
631 mGuideItem->setLine( displayPos.x(), page->scenePos().y(), displayPos.x(), page->scenePos().y() + page->rect().height() );
632 displayPos.setY( 0 );
640 switch ( mOrientation )
645 displayPos.setY( 0 );
651 displayPos.setX( 0 );
660 mHoverGuide = guideAtPoint( event->pos() );
663 setCursor( mOrientation == Qt::Vertical ? Qt::SplitVCursor : Qt::SplitHCursor );
667 setCursor( Qt::ArrowCursor );
671 displayPos = mTransform.inverted().map( event->pos() );
672 switch ( mOrientation )
677 displayPos.setY( 0 );
683 displayPos.setX( 0 );
696 if ( event->button() == Qt::LeftButton )
698 mDraggingGuide = guideAtPoint( event->pos() );
699 if ( !mDraggingGuide )
704 mCreatingGuide =
true;
705 createTemporaryGuideItem();
708 switch ( mOrientation )
712 QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitHCursor : Qt::SplitVCursor );
716 QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitVCursor : Qt::SplitHCursor );
727 if ( event->button() == Qt::LeftButton )
729 if ( mDraggingGuide )
731 QApplication::restoreOverrideCursor();
733 QPointF layoutPoint = convertLocalPointToLayout( event->pos() );
737 bool deleteGuide =
false;
741 if ( layoutPoint.y() < page->scenePos().y() || layoutPoint.y() > page->scenePos().y() + page->rect().height() )
746 if ( layoutPoint.x() < page->scenePos().x() || layoutPoint.x() > page->scenePos().x() + page->rect().width() )
755 mDraggingGuide =
nullptr;
759 mCreatingGuide =
false;
760 QApplication::restoreOverrideCursor();
762 mGuideItem =
nullptr;
765 switch ( mOrientation )
769 if ( event->pos().y() <= height() )
775 if ( event->pos().x() <= width() )
784 QPointF scenePos = convertLocalPointToLayout( event->pos() );
789 std::unique_ptr< QgsLayoutGuide > guide;
790 switch ( mOrientation )
809 else if ( event->button() == Qt::RightButton )
812 mMenu->popup( event->globalPos() );