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 ) );
323 void QgsLayoutRuler::drawGuideAtPos( QPainter *painter, QPoint pos )
325 switch ( mOrientation )
329 painter->translate( pos.x(), 0 );
330 painter->drawPolygon( mGuideMarker );
331 painter->translate( -pos.x(), 0 );
336 painter->translate( 0, pos.y() );
337 painter->drawPolygon( mGuideMarker );
338 painter->translate( 0, -pos.y() );
344 void QgsLayoutRuler::createTemporaryGuideItem()
350 mGuideItem =
new QGraphicsLineItem();
353 QPen linePen( Qt::DotLine );
354 linePen.setColor( QColor( 255, 0, 0, 150 ) );
355 linePen.setWidthF( 0 );
356 mGuideItem->setPen( linePen );
361 QPointF QgsLayoutRuler::convertLocalPointToLayout( QPoint localPoint )
const
363 QPoint viewPoint = mView->mapFromGlobal( mapToGlobal( localPoint ) );
364 return mView->mapToScene( viewPoint );
367 QPoint QgsLayoutRuler::convertLayoutPointToLocal( QPointF layoutPoint )
const
369 QPoint viewPoint = mView->mapFromScene( layoutPoint );
370 return mapFromGlobal( mView->mapToGlobal( viewPoint ) );
373 QgsLayoutGuide *QgsLayoutRuler::guideAtPoint( QPoint localPoint )
const
378 QPointF layoutPoint = convertLocalPointToLayout( localPoint );
379 QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
380 QList< QgsLayoutGuide * > guides = mView->
currentLayout()->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
382 double minDelta = std::numeric_limits<double>::max();
383 const auto constGuides = guides;
386 if ( visiblePages.contains( guide->page() ) )
388 double currentDelta = 0;
389 switch ( mOrientation )
392 currentDelta = std::fabs( layoutPoint.x() - guide->layoutPosition() );
396 currentDelta = std::fabs( layoutPoint.y() - guide->layoutPosition() );
399 if ( currentDelta < minDelta )
401 minDelta = currentDelta;
402 closestGuide = guide;
407 if ( minDelta * mView->transform().m11() <= mDragGuideTolerance )
417 void QgsLayoutRuler::drawRotatedText( QPainter *painter, QPointF pos,
const QString &text )
420 painter->translate( pos.x(), pos.y() );
421 painter->rotate( 270 );
422 painter->drawText( 0, 0, text );
425 void QgsLayoutRuler::drawSmallDivisions( QPainter *painter,
double startPos,
int numDivisions,
double rulerScale,
double maxPos )
427 if ( numDivisions == 0 )
431 double smallMarkerPos = startPos;
432 double smallDivisionSpacing = rulerScale / numDivisions;
434 double pixelCoord = 0.0;
437 for (
int i = 0; i < numDivisions; ++i )
439 smallMarkerPos += smallDivisionSpacing;
441 if ( maxPos > 0 && smallMarkerPos > maxPos )
448 switch ( mOrientation )
452 pixelCoord = mTransform.map( QPointF( smallMarkerPos, 0 ) ).x();
457 pixelCoord = mTransform.map( QPointF( 0, smallMarkerPos ) ).y();
464 if ( ( numDivisions == 10 && i == 4 ) || ( numDivisions == 4 && i == 1 ) )
467 lineSize = mRulerMinSize / 1.5;
471 lineSize = mRulerMinSize / 1.25;
475 switch ( mOrientation )
479 painter->drawLine( pixelCoord, lineSize, pixelCoord, mRulerMinSize );
484 painter->drawLine( lineSize, pixelCoord, mRulerMinSize, pixelCoord );
491 int QgsLayoutRuler::optimumScale(
double minPixelDiff,
int &magnitude,
int &multiple )
496 for (
unsigned int magnitudeCandidate = 0; magnitudeCandidate <
COUNT_VALID_MAGNITUDES; ++magnitudeCandidate )
498 for (
unsigned int multipleCandidate = 0; multipleCandidate <
COUNT_VALID_MULTIPLES; ++multipleCandidate )
500 int candidateScale = VALID_SCALE_MULTIPLES[multipleCandidate] * VALID_SCALE_MAGNITUDES[magnitudeCandidate];
502 double pixelDiff = mTransform.map( QPointF( candidateScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
503 if ( pixelDiff > minPixelDiff )
506 magnitude = VALID_SCALE_MAGNITUDES[magnitudeCandidate];
507 multiple = VALID_SCALE_MULTIPLES[multipleCandidate];
508 return candidateScale;
516 int QgsLayoutRuler::optimumNumberDivisions(
double rulerScale,
int scaleMultiple )
519 double largeDivisionSize = mTransform.map( QPointF( rulerScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
522 QList<int> validSmallDivisions;
523 switch ( scaleMultiple )
528 validSmallDivisions << 10 << 5 << 2;
533 validSmallDivisions << 10 << 4 << 2;
538 validSmallDivisions << 10 << 5;
543 QList<int>::iterator divisions_it;
544 for ( divisions_it = validSmallDivisions.begin(); divisions_it != validSmallDivisions.end(); ++divisions_it )
547 double candidateSize = largeDivisionSize / ( *divisions_it );
549 if ( candidateSize >= mMinPixelsPerDivision )
552 return ( *divisions_it );
563 mTransform = transform;
580 mMarkerPos = mView->mapFromScene( position );
586 mMarkerPos =
event->pos();
593 if ( mCreatingGuide || mDraggingGuide )
596 displayPos = convertLocalPointToLayout( event->pos() );
598 if ( mCreatingGuide )
606 QPen linePen = mGuideItem->pen();
610 linePen.setColor( QColor( 255, 0, 0, 150 ) );
614 linePen.setColor( QColor( 255, 0, 0, 225 ) );
616 mGuideItem->setPen( linePen );
617 switch ( mOrientation )
622 mGuideItem->setLine( page->scenePos().x(), displayPos.y(), page->scenePos().x() + page->rect().width(), displayPos.y() );
623 displayPos.setX( 0 );
629 mGuideItem->setLine( displayPos.x(), page->scenePos().y(), displayPos.x(), page->scenePos().y() + page->rect().height() );
630 displayPos.setY( 0 );
638 switch ( mOrientation )
643 displayPos.setY( 0 );
649 displayPos.setX( 0 );
658 mHoverGuide = guideAtPoint( event->pos() );
661 setCursor( mOrientation == Qt::Vertical ? Qt::SplitVCursor : Qt::SplitHCursor );
665 setCursor( Qt::ArrowCursor );
669 displayPos = mTransform.inverted().map( event->pos() );
670 switch ( mOrientation )
675 displayPos.setY( 0 );
681 displayPos.setX( 0 );
694 if ( event->button() == Qt::LeftButton )
696 mDraggingGuide = guideAtPoint( event->pos() );
697 if ( !mDraggingGuide )
702 mCreatingGuide =
true;
703 createTemporaryGuideItem();
706 switch ( mOrientation )
710 QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitHCursor : Qt::SplitVCursor );
714 QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitVCursor : Qt::SplitHCursor );
725 if ( event->button() == Qt::LeftButton )
727 if ( mDraggingGuide )
729 QApplication::restoreOverrideCursor();
731 QPointF layoutPoint = convertLocalPointToLayout( event->pos() );
735 bool deleteGuide =
false;
739 if ( layoutPoint.y() < page->scenePos().y() || layoutPoint.y() > page->scenePos().y() + page->rect().height() )
744 if ( layoutPoint.x() < page->scenePos().x() || layoutPoint.x() > page->scenePos().x() + page->rect().width() )
753 mDraggingGuide =
nullptr;
757 mCreatingGuide =
false;
758 QApplication::restoreOverrideCursor();
760 mGuideItem =
nullptr;
763 switch ( mOrientation )
767 if ( event->pos().y() <= height() )
773 if ( event->pos().x() <= width() )
782 QPointF scenePos = convertLocalPointToLayout( event->pos() );
787 std::unique_ptr< QgsLayoutGuide > guide;
788 switch ( mOrientation )
807 else if ( event->button() == Qt::RightButton )
810 mMenu->popup( event->globalPos() );