Skip to content

Commit

Permalink
[FEATURE] Add a dock widget showing snap guides for current page
Browse files Browse the repository at this point in the history
Allows creation of snap lines at specific locations, and
adjusting position of existing guides to exact coordinates
  • Loading branch information
nyalldawson committed Aug 7, 2017
1 parent f5126b0 commit ef67275
Show file tree
Hide file tree
Showing 19 changed files with 269 additions and 34 deletions.
6 changes: 6 additions & 0 deletions python/core/layout/qgslayoutguidecollection.sip
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class QgsLayoutGuideCollection : QAbstractListModel
PositionRole,
UnitsRole,
PageRole,
LayoutPositionRole,
};

QgsLayoutGuideCollection( QgsLayout *layout );
Expand Down Expand Up @@ -203,6 +204,11 @@ class QgsLayoutGuideProxyModel : QSortFilterProxyModel
Page numbers begin at 0.
%End

void setPage( int page );
%Docstring
Sets the current ``page`` for filtering matching guides. Page numbers begin at 0.
%End

virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;

virtual bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
Expand Down
14 changes: 14 additions & 0 deletions python/core/layout/qgslayoutsnapper.sip
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ class QgsLayoutSnapper
:rtype: QPointF
%End

double snapPointToGuides( double original, QgsLayoutGuide::Orientation orientation, double scaleFactor, bool &snapped /Out/ ) const;
%Docstring
Snaps a layout coordinate ``point`` to the grid. If ``point``
was snapped, ``snapped`` will be set to true.

The ``scaleFactor`` argument should be set to the transformation from
scalar transform from layout coordinates to pixels, i.e. the
graphics view transform().m11() value.

If snapToGrid() is disabled, this method will return the point
unchanged.
:rtype: float
%End

};

/************************************************************************
Expand Down
21 changes: 19 additions & 2 deletions python/gui/layout/qgslayoutview.sip
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ class QgsLayoutView: QGraphicsView
:rtype: QgsLayoutViewMenuProvider
%End

int currentPage() const;
%Docstring
Returns the page visible in the view. This method
considers the page at the center of the view as the current visible
page.
.. seealso:: pageChanged()
:rtype: int
%End

public slots:

void zoomFull();
Expand Down Expand Up @@ -147,9 +156,9 @@ class QgsLayoutView: QGraphicsView

void emitZoomLevelChanged();

void updateRulers();
void viewChanged();
%Docstring
Updates associated rulers after view extent or zoom has changed.
Updates associated rulers and other widgets after view extent or zoom has changed.
This should be called after calling any of the QGraphicsView
base class methods which alter the view's zoom level or extent,
i.e. QGraphicsView.fitInView().
Expand Down Expand Up @@ -182,6 +191,14 @@ class QgsLayoutView: QGraphicsView
the layout coordinate system.
%End

void pageChanged( int page );
%Docstring
Emitted when the page visible in the view is changed. This signal
considers the page at the center of the view as the current visible
page.
.. seealso:: currentPage()
%End

protected:
virtual void mousePressEvent( QMouseEvent *event );

Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ SET(QGIS_APP_SRCS

layout/qgslayoutaddpagesdialog.cpp
layout/qgslayoutdesignerdialog.cpp
layout/qgslayoutguidewidget.cpp
layout/qgslayoutappmenuprovider.cpp
layout/qgslayoutpagepropertieswidget.cpp
layout/qgslayoutpropertieswidget.cpp
Expand Down Expand Up @@ -339,6 +340,7 @@ SET (QGIS_APP_MOC_HDRS
layout/qgslayoutaddpagesdialog.h
layout/qgslayoutappmenuprovider.h
layout/qgslayoutdesignerdialog.h
layout/qgslayoutguidewidget.h
layout/qgslayoutpagepropertieswidget.h
layout/qgslayoutpropertieswidget.h

Expand Down
22 changes: 21 additions & 1 deletion src/app/layout/qgslayoutdesignerdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "qgspanelwidget.h"
#include "qgsdockwidget.h"
#include "qgslayoutpagepropertieswidget.h"
#include "qgslayoutguidewidget.h"
#include <QShortcut>
#include <QComboBox>
#include <QLineEdit>
Expand Down Expand Up @@ -257,8 +258,20 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
mItemDock->setWidget( mItemPropertiesStack );
mPanelsMenu->addAction( mItemDock->toggleViewAction() );

mGuideDock = new QgsDockWidget( tr( "Guides" ), this );
mGuideDock->setObjectName( QStringLiteral( "GuideDock" ) );
mGuideDock->setMinimumWidth( minDockWidth );
mGuideStack = new QgsPanelWidgetStack();
mGuideDock->setWidget( mGuideStack );
mPanelsMenu->addAction( mGuideDock->toggleViewAction() );
connect( mActionManageGuides, &QAction::triggered, this, [ = ]
{
mGuideDock->setUserVisible( true );
} );

addDockWidget( Qt::RightDockWidgetArea, mItemDock );
addDockWidget( Qt::RightDockWidgetArea, mGeneralDock );
addDockWidget( Qt::RightDockWidgetArea, mGuideDock );

createLayoutPropertiesWidget();

Expand Down Expand Up @@ -317,6 +330,7 @@ void QgsLayoutDesignerDialog::showItemOptions( QgsLayoutItem *item )
delete mItemPropertiesStack->takeMainPanel();
widget->setDockMode( true );
mItemPropertiesStack->setMainPanel( widget.release() );
mItemDock->setUserVisible( true );
}

void QgsLayoutDesignerDialog::open()
Expand Down Expand Up @@ -615,13 +629,19 @@ void QgsLayoutDesignerDialog::createLayoutPropertiesWidget()
return;
}

// update composition widget
// update layout based widgets
QgsLayoutPropertiesWidget *oldCompositionWidget = qobject_cast<QgsLayoutPropertiesWidget *>( mGeneralPropertiesStack->takeMainPanel() );
delete oldCompositionWidget;
QgsLayoutGuideWidget *oldGuideWidget = qobject_cast<QgsLayoutGuideWidget *>( mGuideStack->takeMainPanel() );
delete oldGuideWidget;

QgsLayoutPropertiesWidget *widget = new QgsLayoutPropertiesWidget( mGeneralDock, mLayout );
widget->setDockMode( true );
mGeneralPropertiesStack->setMainPanel( widget );

QgsLayoutGuideWidget *guideWidget = new QgsLayoutGuideWidget( mGuideDock, mLayout, mView );
guideWidget->setDockMode( true );
mGuideStack->setMainPanel( guideWidget );
}

void QgsLayoutDesignerDialog::initializeRegistry()
Expand Down
2 changes: 2 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
QgsPanelWidgetStack *mItemPropertiesStack = nullptr;
QgsDockWidget *mGeneralDock = nullptr;
QgsPanelWidgetStack *mGeneralPropertiesStack = nullptr;
QgsDockWidget *mGuideDock = nullptr;
QgsPanelWidgetStack *mGuideStack = nullptr;

//! Save window state
void saveWindowState();
Expand Down
6 changes: 6 additions & 0 deletions src/core/layout/qgslayoutguidecollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,12 @@ QgsLayoutGuideProxyModel::QgsLayoutGuideProxyModel( QObject *parent, QgsLayoutGu
sort( 0 );
}

void QgsLayoutGuideProxyModel::setPage( int page )
{
mPage = page;
invalidateFilter();
}

bool QgsLayoutGuideProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
{
QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
Expand Down
6 changes: 6 additions & 0 deletions src/core/layout/qgslayoutguidecollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class CORE_EXPORT QgsLayoutGuideCollection : public QAbstractListModel
PositionRole, //!< Guide position role
UnitsRole, //!< Guide position units role
PageRole, //!< Guide page role
LayoutPositionRole, //!< Guide position in layout coordinates
};

/**
Expand Down Expand Up @@ -234,6 +235,11 @@ class CORE_EXPORT QgsLayoutGuideProxyModel : public QSortFilterProxyModel
*/
explicit QgsLayoutGuideProxyModel( QObject *parent SIP_TRANSFERTHIS, QgsLayoutGuide::Orientation orientation, int page );

/**
* Sets the current \a page for filtering matching guides. Page numbers begin at 0.
*/
void setPage( int page );

bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override;
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override;

Expand Down
47 changes: 47 additions & 0 deletions src/core/layout/qgslayoutsnapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ QPointF QgsLayoutSnapper::snapPoint( QPointF point, double scaleFactor, bool &sn
return res;
}

bool snappedToHozGuides = false;
double newX = snapPointToGuides( point.x(), QgsLayoutGuide::Vertical, scaleFactor, snappedToHozGuides );
if ( snappedToHozGuides )
{
snapped = true;
point.setX( newX );
}
bool snappedToVertGuides = false;
double newY = snapPointToGuides( point.y(), QgsLayoutGuide::Horizontal, scaleFactor, snappedToVertGuides );
if ( snappedToVertGuides )
{
snapped = true;
point.setY( newY );
}

return point;
}

Expand Down Expand Up @@ -88,3 +103,35 @@ QPointF QgsLayoutSnapper::snapPointToGrid( QPointF point, double scaleFactor, bo

return QPointF( xSnapped, ySnapped );
}

double QgsLayoutSnapper::snapPointToGuides( double original, QgsLayoutGuide::Orientation orientation, double scaleFactor, bool &snapped ) const
{
snapped = false;

//convert snap tolerance from pixels to layout units
double alignThreshold = mTolerance / scaleFactor;

double bestPos = original;
double smallestDiff = DBL_MAX;

Q_FOREACH ( QgsLayoutGuide *guide, mLayout->guides().guides( orientation ) )
{
double guidePos = guide->layoutPosition();
double diff = qAbs( original - guidePos );
if ( diff < smallestDiff )
{
smallestDiff = diff;
bestPos = guidePos;
}
}

if ( smallestDiff <= alignThreshold )
{
snapped = true;
return bestPos;
}
else
{
return original;
}
}
14 changes: 14 additions & 0 deletions src/core/layout/qgslayoutsnapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "qgis_core.h"
#include "qgslayoutmeasurement.h"
#include "qgslayoutpoint.h"
#include "qgslayoutguidecollection.h"
#include <QPen>

class QgsLayout;
Expand Down Expand Up @@ -88,6 +89,19 @@ class CORE_EXPORT QgsLayoutSnapper
*/
QPointF snapPointToGrid( QPointF point, double scaleFactor, bool &snapped SIP_OUT ) const;

/**
* Snaps a layout coordinate \a point to the grid. If \a point
* was snapped, \a snapped will be set to true.
*
* The \a scaleFactor argument should be set to the transformation from
* scalar transform from layout coordinates to pixels, i.e. the
* graphics view transform().m11() value.
*
* If snapToGrid() is disabled, this method will return the point
* unchanged.
*/
double snapPointToGuides( double original, QgsLayoutGuide::Orientation orientation, double scaleFactor, bool &snapped SIP_OUT ) const;

private:

QgsLayout *mLayout = nullptr;
Expand Down
36 changes: 26 additions & 10 deletions src/gui/layout/qgslayoutview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ void QgsLayoutView::setCurrentLayout( QgsLayout *layout )
{
setScene( layout );

connect( layout->pageCollection(), &QgsLayoutPageCollection::changed, this, &QgsLayoutView::updateRulers );
updateRulers();
connect( layout->pageCollection(), &QgsLayoutPageCollection::changed, this, &QgsLayoutView::viewChanged );
viewChanged();

mSnapMarker.reset( new QgsLayoutViewSnapMarker() );
mSnapMarker->hide();
Expand Down Expand Up @@ -114,7 +114,7 @@ void QgsLayoutView::scaleSafe( double scale )
scale = qBound( MIN_VIEW_SCALE, scale, MAX_VIEW_SCALE );
setTransform( QTransform::fromScale( scale, scale ) );
emit zoomLevelChanged();
updateRulers();
viewChanged();
}

void QgsLayoutView::setZoomLevel( double level )
Expand All @@ -136,21 +136,21 @@ void QgsLayoutView::setZoomLevel( double level )
setTransform( QTransform::fromScale( mmLevel, mmLevel ) );
}
emit zoomLevelChanged();
updateRulers();
viewChanged();
}

void QgsLayoutView::setHorizontalRuler( QgsLayoutRuler *ruler )
{
mHorizontalRuler = ruler;
ruler->setLayoutView( this );
updateRulers();
viewChanged();
}

void QgsLayoutView::setVerticalRuler( QgsLayoutRuler *ruler )
{
mVerticalRuler = ruler;
ruler->setLayoutView( this );
updateRulers();
viewChanged();
}

void QgsLayoutView::setMenuProvider( QgsLayoutViewMenuProvider *provider )
Expand All @@ -166,7 +166,7 @@ QgsLayoutViewMenuProvider *QgsLayoutView::menuProvider() const
void QgsLayoutView::zoomFull()
{
fitInView( scene()->sceneRect(), Qt::KeepAspectRatio );
updateRulers();
viewChanged();
emit zoomLevelChanged();
}

Expand All @@ -189,7 +189,7 @@ void QgsLayoutView::zoomWidth()

fitInView( targetRect, Qt::KeepAspectRatio );
emit zoomLevelChanged();
updateRulers();
viewChanged();
}

void QgsLayoutView::zoomIn()
Expand Down Expand Up @@ -363,15 +363,16 @@ void QgsLayoutView::resizeEvent( QResizeEvent *event )
{
QGraphicsView::resizeEvent( event );
emit zoomLevelChanged();
viewChanged();
}

void QgsLayoutView::scrollContentsBy( int dx, int dy )
{
QGraphicsView::scrollContentsBy( dx, dy );
updateRulers();
viewChanged();
}

void QgsLayoutView::updateRulers()
void QgsLayoutView::viewChanged()
{
if ( mHorizontalRuler )
{
Expand All @@ -381,6 +382,21 @@ void QgsLayoutView::updateRulers()
{
mVerticalRuler->setSceneTransform( viewportTransform() );
}

// determine page at center of view
QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
QRectF visibleRect = mapToScene( viewportRect ).boundingRect();
QPointF centerVisible = visibleRect.center();

if ( currentLayout() && currentLayout()->pageCollection() )
{
int newPage = currentLayout()->pageCollection()->pageNumberForPoint( centerVisible );
if ( newPage != mCurrentPage )
{
mCurrentPage = newPage;
emit pageChanged( mCurrentPage );
}
}
}

void QgsLayoutView::wheelZoom( QWheelEvent *event )
Expand Down
Loading

0 comments on commit ef67275

Please sign in to comment.