Skip to content

Commit

Permalink
Port item alignment to layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Oct 6, 2017
1 parent c6da276 commit 0939333
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 2 deletions.
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
%Include composer/qgscomposermultiframecommand.sip
%Include composer/qgscomposertexttable.sip
%Include composer/qgspaperitem.sip
%Include layout/qgslayoutaligner.sip
%Include layout/qgslayoutcontext.sip
%Include layout/qgslayoutgridsettings.sip
%Include layout/qgslayoutmeasurement.sip
Expand Down
52 changes: 52 additions & 0 deletions python/core/layout/qgslayoutaligner.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/layout/qgslayoutaligner.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/



class QgsLayoutAligner
{
%Docstring
Handles aligning and distributing sets of layout items.

QgsLayoutAligner contains methods for automatically aligning and distributing
sets of layout items, e.g. aligning a group of items to top or left sides.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgslayoutaligner.h"
%End
public:

enum Alignment
{
Left,
HCenter,
Right,
Top,
VCenter,
Bottom,
};

static void alignItems( QgsLayout *layout, const QList< QgsLayoutItem * > &items, Alignment alignment );
%Docstring
Aligns a set of ``items`` from a ``layout`` in place.

The ``alignment`` argument specifies the method to use when aligning the items.
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/layout/qgslayoutaligner.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
5 changes: 5 additions & 0 deletions python/gui/layout/qgslayoutview.sip
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ class QgsLayoutView: QGraphicsView
:rtype: list of int
%End

void alignSelectedItems( QgsLayoutAligner::Alignment alignment );
%Docstring
Aligns all selected items using the specified ``alignment``.
%End

public slots:

void zoomFull();
Expand Down
38 changes: 38 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,20 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
orderingToolButton->setDefaultAction( mActionRaiseItems );
mActionsToolbar->addWidget( orderingToolButton );

QToolButton *alignToolButton = new QToolButton( this );
alignToolButton->setPopupMode( QToolButton::InstantPopup );
alignToolButton->setAutoRaise( true );
alignToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );

alignToolButton->addAction( mActionAlignLeft );
alignToolButton->addAction( mActionAlignHCenter );
alignToolButton->addAction( mActionAlignRight );
alignToolButton->addAction( mActionAlignTop );
alignToolButton->addAction( mActionAlignVCenter );
alignToolButton->addAction( mActionAlignBottom );
alignToolButton->setDefaultAction( mActionAlignLeft );
mActionsToolbar->addWidget( alignToolButton );

mAddItemTool = new QgsLayoutViewToolAddItem( mView );
mPanTool = new QgsLayoutViewToolPan( mView );
mPanTool->setAction( mActionPan );
Expand Down Expand Up @@ -213,6 +227,30 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionLowerItems, &QAction::triggered, this, &QgsLayoutDesignerDialog::lowerSelectedItems );
connect( mActionMoveItemsToTop, &QAction::triggered, this, &QgsLayoutDesignerDialog::moveSelectedItemsToTop );
connect( mActionMoveItemsToBottom, &QAction::triggered, this, &QgsLayoutDesignerDialog::moveSelectedItemsToBottom );
connect( mActionAlignLeft, &QAction::triggered, this, [ = ]
{
mView->alignSelectedItems( QgsLayoutAligner::Left );
} );
connect( mActionAlignHCenter, &QAction::triggered, this, [ = ]
{
mView->alignSelectedItems( QgsLayoutAligner::HCenter );
} );
connect( mActionAlignRight, &QAction::triggered, this, [ = ]
{
mView->alignSelectedItems( QgsLayoutAligner::Right );
} );
connect( mActionAlignTop, &QAction::triggered, this, [ = ]
{
mView->alignSelectedItems( QgsLayoutAligner::Top );
} );
connect( mActionAlignVCenter, &QAction::triggered, this, [ = ]
{
mView->alignSelectedItems( QgsLayoutAligner::VCenter );
} );
connect( mActionAlignBottom, &QAction::triggered, this, [ = ]
{
mView->alignSelectedItems( QgsLayoutAligner::Bottom );
} );

connect( mActionAddPages, &QAction::triggered, this, &QgsLayoutDesignerDialog::addPages );

Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ SET(QGIS_CORE_SRCS
dxf/qgsdxfpallabeling.cpp

layout/qgslayout.cpp
layout/qgslayoutaligner.cpp
layout/qgslayoutcontext.cpp
layout/qgslayoutgridsettings.cpp
layout/qgslayoutguidecollection.cpp
Expand Down Expand Up @@ -965,6 +966,7 @@ SET(QGIS_CORE_HDRS
composer/qgscomposertexttable.h
composer/qgspaperitem.h

layout/qgslayoutaligner.h
layout/qgslayoutcontext.h
layout/qgslayoutgridsettings.h
layout/qgslayoutitemundocommand.h
Expand Down
131 changes: 131 additions & 0 deletions src/core/layout/qgslayoutaligner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/***************************************************************************
qgslayoutaligner.cpp
--------------------
begin : October 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgslayoutaligner.h"
#include "qgslayoutitem.h"
#include "qgslayout.h"

void QgsLayoutAligner::alignItems( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Alignment alignment )
{
if ( !layout || items.size() < 2 )
{
return;
}

QRectF itemBBox = boundingRectOfItems( items );
if ( !itemBBox.isValid() )
{
return;
}

double refCoord = 0;
switch ( alignment )
{
case Left:
refCoord = itemBBox.left();
break;
case HCenter:
refCoord = itemBBox.center().x();
break;
case Right:
refCoord = itemBBox.right();
break;
case Top:
refCoord = itemBBox.top();
break;
case VCenter:
refCoord = itemBBox.center().y();
break;
case Bottom:
refCoord = itemBBox.bottom();
break;
}

layout->undoStack()->beginMacro( QObject::tr( "Aligned items bottom" ) );
for ( QgsLayoutItem *item : items )
{
layout->undoStack()->beginCommand( item, QString() );

QPointF shifted = item->pos();
switch ( alignment )
{
case Left:
shifted.setX( refCoord );
break;
case HCenter:
shifted.setX( refCoord - item->rect().width() / 2.0 );
break;
case Right:
shifted.setX( refCoord - item->rect().width() );
break;
case Top:
shifted.setY( refCoord );
break;
case VCenter:
shifted.setY( refCoord - item->rect().height() / 2.0 );
break;
case Bottom:
shifted.setY( refCoord - item->rect().height() );
break;
}

// need to keep item units
QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
item->attemptMove( newPos );

layout->undoStack()->endCommand();
}
layout->undoStack()->endMacro();
}

QRectF QgsLayoutAligner::boundingRectOfItems( const QList<QgsLayoutItem *> &items )
{
if ( items.empty() )
{
return QRectF();
}

auto it = items.constBegin();
//set the box to the first item
QgsLayoutItem *currentItem = *it;
it++;
double minX = currentItem->pos().x();
double minY = currentItem->pos().y();
double maxX = minX + currentItem->rect().width();
double maxY = minY + currentItem->rect().height();

double currentMinX, currentMinY, currentMaxX, currentMaxY;

for ( ; it != items.constEnd(); ++it )
{
currentItem = *it;
currentMinX = currentItem->pos().x();
currentMinY = currentItem->pos().y();
currentMaxX = currentMinX + currentItem->rect().width();
currentMaxY = currentMinY + currentItem->rect().height();

if ( currentMinX < minX )
minX = currentMinX;
if ( currentMaxX > maxX )
maxX = currentMaxX;
if ( currentMinY < minY )
minY = currentMinY;
if ( currentMaxY > maxY )
maxY = currentMaxY;
}

return QRectF( QPointF( minX, minY ), QPointF( maxX, maxY ) );
}
71 changes: 71 additions & 0 deletions src/core/layout/qgslayoutaligner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/***************************************************************************
qgslayoutaligner.h
------------------
begin : October 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSLAYOUTALIGNER_H
#define QGSLAYOUTALIGNER_H

#include "qgis_core.h"
#include <QList>
#include <QRectF>

class QgsLayoutItem;
class QgsLayout;

/**
* \ingroup core
* \class QgsLayoutAligner
* \brief Handles aligning and distributing sets of layout items.
*
* QgsLayoutAligner contains methods for automatically aligning and distributing
* sets of layout items, e.g. aligning a group of items to top or left sides.
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLayoutAligner
{

public:

//! Alignment options
enum Alignment
{
Left, //!< Align left edges
HCenter, //!< Align horizontal centers
Right, //!< Align right edges
Top, //!< Align top edges
VCenter, //!< Align vertical centers
Bottom, //!< Align bottom edges
};

/**
* Aligns a set of \a items from a \a layout in place.
*
* The \a alignment argument specifies the method to use when aligning the items.
*/
static void alignItems( QgsLayout *layout, const QList< QgsLayoutItem * > &items, Alignment alignment );

private:

/**
* Returns the bounding rectangle of the selected items in
* scene coordinates.
*/
static QRectF boundingRectOfItems( const QList< QgsLayoutItem * > &items );



};

#endif //QGSLAYOUTALIGNER_H
6 changes: 6 additions & 0 deletions src/gui/layout/qgslayoutview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ QList<int> QgsLayoutView::visiblePageNumbers() const
return currentLayout()->pageCollection()->visiblePageNumbers( visibleRect );
}

void QgsLayoutView::alignSelectedItems( QgsLayoutAligner::Alignment alignment )
{
const QList<QgsLayoutItem *> selectedItems = currentLayout()->selectedLayoutItems();
QgsLayoutAligner::alignItems( currentLayout(), selectedItems, alignment );
}

void QgsLayoutView::zoomFull()
{
fitInView( scene()->sceneRect(), Qt::KeepAspectRatio );
Expand Down
6 changes: 6 additions & 0 deletions src/gui/layout/qgslayoutview.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "qgsprevieweffect.h" // for QgsPreviewEffect::PreviewMode
#include "qgis_gui.h"
#include "qgslayoutitempage.h"
#include "qgslayoutaligner.h"
#include <QPointer>
#include <QGraphicsView>
#include <QGraphicsRectItem>
Expand Down Expand Up @@ -157,6 +158,11 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
*/
QList< int > visiblePageNumbers() const;

/**
* Aligns all selected items using the specified \a alignment.
*/
void alignSelectedItems( QgsLayoutAligner::Alignment alignment );

public slots:

/**
Expand Down
Loading

0 comments on commit 0939333

Please sign in to comment.