Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Support dragging existing guides in rulers
  • Loading branch information
nyalldawson committed Aug 7, 2017
1 parent c6c9c6f commit e06b0af
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 68 deletions.
7 changes: 7 additions & 0 deletions python/core/layout/qgslayoutguidecollection.sip
Expand Up @@ -115,9 +115,16 @@ class QgsLayoutGuide : QObject
double layoutPosition() const;
%Docstring
Returns the guide's position in absolute layout units.
.. seealso:: setLayoutPosition()
:rtype: float
%End

void setLayoutPosition( double position );
%Docstring
Sets the guide's ``position`` in absolute layout units.
.. seealso:: layoutPosition()
%End

signals:

void positionChanged();
Expand Down
18 changes: 18 additions & 0 deletions src/core/layout/qgslayoutguidecollection.cpp
Expand Up @@ -125,6 +125,24 @@ double QgsLayoutGuide::layoutPosition() const
return -999; // avoid warning
}

void QgsLayoutGuide::setLayoutPosition( double position )
{
double p = 0;
switch ( mOrientation )
{
case Horizontal:
p = mLineItem->mapFromScene( QPointF( 0, position ) ).y();
break;

case Vertical:
p = mLineItem->mapFromScene( QPointF( position, 0 ) ).x();
break;
}
mPosition = mLayout->convertFromLayoutUnits( p, mPosition.units() );
update();
emit positionChanged();
}

QgsLayout *QgsLayoutGuide::layout() const
{
return mLayout;
Expand Down
7 changes: 7 additions & 0 deletions src/core/layout/qgslayoutguidecollection.h
Expand Up @@ -128,9 +128,16 @@ class CORE_EXPORT QgsLayoutGuide : public QObject

/**
* Returns the guide's position in absolute layout units.
* \see setLayoutPosition()
*/
double layoutPosition() const;

/**
* Sets the guide's \a position in absolute layout units.
* \see layoutPosition()
*/
void setLayoutPosition( double position );

signals:

/**
Expand Down
205 changes: 137 additions & 68 deletions src/gui/layout/qgslayoutruler.cpp
Expand Up @@ -55,6 +55,7 @@ QgsLayoutRuler::QgsLayoutRuler( QWidget *parent, Qt::Orientation orientation )
mMinSpacingVerticalLabels = mRulerMinSize / 5;

double guideMarkerSize = mRulerFontMetrics->width( "*" );
mDragGuideTolerance = guideMarkerSize;
switch ( mOrientation )
{
case Qt::Horizontal:
Expand Down Expand Up @@ -521,40 +522,62 @@ void QgsLayoutRuler::mouseMoveEvent( QMouseEvent *event )
update();

QPointF displayPos;
if ( mCreatingGuide )
if ( mCreatingGuide || mDraggingGuide )
{
// event -> layout coordinates
displayPos = convertLocalPointToLayout( event->pos() );

QgsLayout *layout = mView->currentLayout();
int pageNo = layout->pageCollection()->pageNumberForPoint( displayPos );
QgsLayoutItemPage *page = layout->pageCollection()->page( pageNo );
QPen linePen = mGuideItem->pen();
// if guide preview is outside a page draw it a lot fainter, to indicate it's invalid
if ( !layout->pageCollection()->pageAtPoint( displayPos ) )
if ( mCreatingGuide )
{
linePen.setColor( QColor( 255, 0, 0, 150 ) );
QgsLayout *layout = mView->currentLayout();
int pageNo = layout->pageCollection()->pageNumberForPoint( displayPos );
QgsLayoutItemPage *page = layout->pageCollection()->page( pageNo );
QPen linePen = mGuideItem->pen();
// if guide preview is outside a page draw it a lot fainter, to indicate it's invalid
if ( !layout->pageCollection()->pageAtPoint( displayPos ) )
{
linePen.setColor( QColor( 255, 0, 0, 150 ) );
}
else
{
linePen.setColor( QColor( 255, 0, 0, 225 ) );
}
mGuideItem->setPen( linePen );
switch ( mOrientation )
{
case Qt::Horizontal:
{
//mouse is creating a horizontal ruler, so don't show x coordinate
mGuideItem->setLine( page->scenePos().x(), displayPos.y(), page->scenePos().x() + page->rect().width(), displayPos.y() );
displayPos.setX( 0 );
break;
}
case Qt::Vertical:
{
//mouse is creating a vertical ruler, so don't show a y coordinate
mGuideItem->setLine( displayPos.x(), page->scenePos().y(), displayPos.x(), page->scenePos().y() + page->rect().height() );
displayPos.setY( 0 );
break;
}
}
}
else
{
linePen.setColor( QColor( 255, 0, 0, 225 ) );
}
mGuideItem->setPen( linePen );
switch ( mOrientation )
{
case Qt::Horizontal:
{
//mouse is creating a horizontal ruler, so don't show x coordinate
mGuideItem->setLine( page->scenePos().x(), displayPos.y(), page->scenePos().x() + page->rect().width(), displayPos.y() );
displayPos.setX( 0 );
break;
}
case Qt::Vertical:
// dragging guide
switch ( mOrientation )
{
//mouse is creating a vertical ruler, so don't show a y coordinate
mGuideItem->setLine( displayPos.x(), page->scenePos().y(), displayPos.x(), page->scenePos().y() + page->rect().height() );
displayPos.setY( 0 );
break;
case Qt::Horizontal:
{
mDraggingGuide->setLayoutPosition( displayPos.x() );
displayPos.setY( 0 );
break;
}
case Qt::Vertical:
{
mDraggingGuide->setLayoutPosition( displayPos.y() );
displayPos.setX( 0 );
break;
}
}
}
}
Expand Down Expand Up @@ -585,18 +608,53 @@ void QgsLayoutRuler::mousePressEvent( QMouseEvent *event )
{
if ( event->button() == Qt::LeftButton )
{
mCreatingGuide = true;
createTemporaryGuideItem();
// was click on an existing guide? huh?? was it??
QPointF layoutPoint = convertLocalPointToLayout( event->pos() );
QList< int > visiblePageNumbers = mView->visiblePageNumbers();
QList< QgsLayoutGuide * > guides = mView->currentLayout()->guides().guides( mOrientation == Qt::Horizontal ? QgsLayoutGuide::Vertical : QgsLayoutGuide::Horizontal );
QgsLayoutGuide *closestGuide = nullptr;
double minDelta = DBL_MAX;
Q_FOREACH ( QgsLayoutGuide *guide, guides )
{
if ( visiblePageNumbers.contains( guide->page() ) )
{
double currentDelta = 0;
switch ( mOrientation )
{
case Qt::Horizontal:
currentDelta = qAbs( layoutPoint.x() - guide->layoutPosition() );
break;

case Qt::Vertical:
currentDelta = qAbs( layoutPoint.y() - guide->layoutPosition() );
break;
}
if ( currentDelta < minDelta )
{
minDelta = currentDelta;
closestGuide = guide;
}
}
}

if ( minDelta * mView->transform().m11() <= mDragGuideTolerance )
{
mDraggingGuide = closestGuide;
}
else
{
mCreatingGuide = true;
createTemporaryGuideItem();
}
switch ( mOrientation )
{
case Qt::Horizontal:
{
QApplication::setOverrideCursor( Qt::SplitVCursor );
QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitHCursor : Qt::SplitVCursor );
break;
}
case Qt::Vertical:
QApplication::setOverrideCursor( Qt::SplitHCursor );
QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitVCursor : Qt::SplitHCursor );
break;
}
}
Expand All @@ -606,54 +664,65 @@ void QgsLayoutRuler::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() == Qt::LeftButton )
{
mCreatingGuide = false;
QApplication::restoreOverrideCursor();
mGuideItem.reset();
if ( mDraggingGuide )
{
QApplication::restoreOverrideCursor();

// check that cursor left the ruler
switch ( mOrientation )
// TODO - delete guide if outside of page

mDraggingGuide = nullptr;
}
else
{
case Qt::Horizontal:
{
if ( event->pos().y() <= height() )
return;
break;
}
case Qt::Vertical:
mCreatingGuide = false;
QApplication::restoreOverrideCursor();
mGuideItem.reset();

// check that cursor left the ruler
switch ( mOrientation )
{
if ( event->pos().x() <= width() )
return;
break;
case Qt::Horizontal:
{
if ( event->pos().y() <= height() )
return;
break;
}
case Qt::Vertical:
{
if ( event->pos().x() <= width() )
return;
break;
}
}
}

QgsLayout *layout = mView->currentLayout();
QgsLayout *layout = mView->currentLayout();

// create guide
QPointF scenePos = convertLocalPointToLayout( event->pos() );
QgsLayoutItemPage *page = layout->pageCollection()->pageAtPoint( scenePos );
if ( !page )
return; // dragged outside of a page
// create guide
QPointF scenePos = convertLocalPointToLayout( event->pos() );
QgsLayoutItemPage *page = layout->pageCollection()->pageAtPoint( scenePos );
if ( !page )
return; // dragged outside of a page

int pageNumber = layout->pageCollection()->pageNumber( page );
std::unique_ptr< QgsLayoutGuide > guide;
switch ( mOrientation )
{
case Qt::Horizontal:
{
//mouse is creating a horizontal guide
double posOnPage = layout->pageCollection()->positionOnPage( scenePos ).y();
guide.reset( new QgsLayoutGuide( QgsLayoutGuide::Horizontal, QgsLayoutMeasurement( posOnPage, layout->units() ) ) );
break;
}
case Qt::Vertical:
int pageNumber = layout->pageCollection()->pageNumber( page );
std::unique_ptr< QgsLayoutGuide > guide;
switch ( mOrientation )
{
//mouse is creating a vertical guide
guide.reset( new QgsLayoutGuide( QgsLayoutGuide::Vertical, QgsLayoutMeasurement( scenePos.x(), layout->units() ) ) );
break;
case Qt::Horizontal:
{
//mouse is creating a horizontal guide
double posOnPage = layout->pageCollection()->positionOnPage( scenePos ).y();
guide.reset( new QgsLayoutGuide( QgsLayoutGuide::Horizontal, QgsLayoutMeasurement( posOnPage, layout->units() ) ) );
break;
}
case Qt::Vertical:
{
//mouse is creating a vertical guide
guide.reset( new QgsLayoutGuide( QgsLayoutGuide::Vertical, QgsLayoutMeasurement( scenePos.x(), layout->units() ) ) );
break;
}
}
guide->setPage( pageNumber );
mView->currentLayout()->guides().addGuide( guide.release() );
}
guide->setPage( pageNumber );
mView->currentLayout()->guides().addGuide( guide.release() );
}
}
4 changes: 4 additions & 0 deletions src/gui/layout/qgslayoutruler.h
Expand Up @@ -22,6 +22,7 @@
class QgsLayout;
class QGraphicsLineItem;
class QgsLayoutView;
class QgsLayoutGuide;

/**
* \ingroup gui
Expand Down Expand Up @@ -108,6 +109,9 @@ class GUI_EXPORT QgsLayoutRuler: public QWidget
int mTextBaseline;
int mMinSpacingVerticalLabels;

int mDragGuideTolerance = 0;
QgsLayoutGuide *mDraggingGuide = nullptr;

bool mCreatingGuide = false;
std::unique_ptr< QGraphicsLineItem > mGuideItem;

Expand Down
1 change: 1 addition & 0 deletions tests/src/python/test_qgslayoutguides.py
Expand Up @@ -104,6 +104,7 @@ def testUpdateGuide(self):
g.update()
self.assertFalse(g.item().isVisible())


def testCollection(self):
p = QgsProject()
l = QgsLayout(p)
Expand Down

0 comments on commit e06b0af

Please sign in to comment.