Port item alignment to layouts

This commit is contained in:
Nyall Dawson 2017-10-03 21:48:18 +10:00
parent c6da276783
commit 0939333273
10 changed files with 393 additions and 2 deletions

View File

@ -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

View File

@ -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 *
************************************************************************/

View File

@ -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();

View File

@ -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 );
@ -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 );

View File

@ -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
@ -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

View File

@ -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 ) );
}

View File

@ -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

View File

@ -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 );

View File

@ -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>
@ -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:
/**

View File

@ -85,7 +85,7 @@
<x>0</x>
<y>0</y>
<width>1083</width>
<height>25</height>
<height>42</height>
</rect>
</property>
<widget class="QMenu" name="mLayoutMenu">
@ -160,8 +160,16 @@
<addaction name="mActionLowerItems"/>
<addaction name="mActionMoveItemsToTop"/>
<addaction name="mActionMoveItemsToBottom"/>
<addaction name="separator"/>
<addaction name="mActionLockItems"/>
<addaction name="mActionUnlockAll"/>
<addaction name="separator"/>
<addaction name="mActionAlignLeft"/>
<addaction name="mActionAlignHCenter"/>
<addaction name="mActionAlignRight"/>
<addaction name="mActionAlignTop"/>
<addaction name="mActionAlignVCenter"/>
<addaction name="mActionAlignBottom"/>
</widget>
<addaction name="mLayoutMenu"/>
<addaction name="menuEdit"/>
@ -663,6 +671,78 @@
<string>Ctrl+Shift+[</string>
</property>
</action>
<action name="mActionAlignLeft">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionAlignLeft.svg</normaloff>:/images/themes/default/mActionAlignLeft.svg</iconset>
</property>
<property name="text">
<string>Align Left</string>
</property>
<property name="toolTip">
<string>Align selected items left</string>
</property>
</action>
<action name="mActionAlignHCenter">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionAlignHCenter.svg</normaloff>:/images/themes/default/mActionAlignHCenter.svg</iconset>
</property>
<property name="text">
<string>Align Center</string>
</property>
<property name="toolTip">
<string>Align center horizontal</string>
</property>
</action>
<action name="mActionAlignRight">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionAlignRight.svg</normaloff>:/images/themes/default/mActionAlignRight.svg</iconset>
</property>
<property name="text">
<string>Align Right</string>
</property>
<property name="toolTip">
<string>Align selected items right</string>
</property>
</action>
<action name="mActionAlignTop">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionAlignTop.svg</normaloff>:/images/themes/default/mActionAlignTop.svg</iconset>
</property>
<property name="text">
<string>Align Top</string>
</property>
<property name="toolTip">
<string>Align selected items to top</string>
</property>
</action>
<action name="mActionAlignVCenter">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionAlignVCenter.svg</normaloff>:/images/themes/default/mActionAlignVCenter.svg</iconset>
</property>
<property name="text">
<string>Align Center Vertical</string>
</property>
<property name="toolTip">
<string>Align center vertical</string>
</property>
</action>
<action name="mActionAlignBottom">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionAlignBottom.svg</normaloff>:/images/themes/default/mActionAlignBottom.svg</iconset>
</property>
<property name="text">
<string>Align Bottom</string>
</property>
<property name="toolTip">
<string>Align selected items bottom</string>
</property>
</action>
</widget>
<resources>
<include location="../../../images/images.qrc"/>
@ -691,7 +771,6 @@
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
</resources>
<connections/>
</ui>