Skip to content

Commit

Permalink
Work on resizing layouts to item bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 17, 2017
1 parent f4f5f75 commit fe5bd47
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 38 deletions.
10 changes: 10 additions & 0 deletions python/core/layout/qgslayoutpagecollection.sip
Expand Up @@ -282,6 +282,16 @@ Returns the space between pages, in layout units.
Returns the size of the page shadow, in layout units.
%End

void resizeToContents( const QgsMargins &margins, QgsUnitTypes::LayoutUnit marginUnits );
%Docstring
Resizes the layout to a single page which fits the current contents of the layout.

Calling this method resets the number of pages to 1, with the size set to the
minimum size required to fit all existing layout items. Items will also be
repositioned so that the new top-left bounds of the layout is at the point
(marginLeft, marginTop). An optional margin can be specified.
%End

virtual bool writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const;

%Docstring
Expand Down
49 changes: 49 additions & 0 deletions src/app/layout/qgslayoutpropertieswidget.cpp
Expand Up @@ -18,11 +18,14 @@
#include "qgslayout.h"
#include "qgslayoutsnapper.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutundostack.h"

QgsLayoutPropertiesWidget::QgsLayoutPropertiesWidget( QWidget *parent, QgsLayout *layout )
: QgsPanelWidget( parent )
, mLayout( layout )
{
Q_ASSERT( mLayout );

setupUi( this );
setPanelTitle( tr( "Layout properties" ) );
blockSignals( true );
Expand All @@ -42,6 +45,30 @@ QgsLayoutPropertiesWidget::QgsLayoutPropertiesWidget( QWidget *parent, QgsLayout
connect( mGridResolutionSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::gridResolutionChanged );
connect( mOffsetXSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::gridOffsetXChanged );
connect( mOffsetYSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::gridOffsetYChanged );

double leftMargin = mLayout->customProperty( QStringLiteral( "resizeToContentsLeftMargin" ) ).toDouble();
double topMargin = mLayout->customProperty( QStringLiteral( "resizeToContentsTopMargin" ) ).toDouble();
double bottomMargin = mLayout->customProperty( QStringLiteral( "resizeToContentsBottomMargin" ) ).toDouble();
double rightMargin = mLayout->customProperty( QStringLiteral( "resizeToContentsRightMargin" ) ).toDouble();
QgsUnitTypes::LayoutUnit marginUnit = static_cast< QgsUnitTypes::LayoutUnit >(
mLayout->customProperty( QStringLiteral( "imageCropMarginUnit" ), QgsUnitTypes::LayoutMillimeters ).toInt() );

mTopMarginSpinBox->setValue( topMargin );
mMarginUnitsComboBox->linkToWidget( mTopMarginSpinBox );
mRightMarginSpinBox->setValue( rightMargin );
mMarginUnitsComboBox->linkToWidget( mRightMarginSpinBox );
mBottomMarginSpinBox->setValue( bottomMargin );
mMarginUnitsComboBox->linkToWidget( mBottomMarginSpinBox );
mLeftMarginSpinBox->setValue( leftMargin );
mMarginUnitsComboBox->linkToWidget( mLeftMarginSpinBox );
mMarginUnitsComboBox->setUnit( marginUnit );
mMarginUnitsComboBox->setConverter( &mLayout->context().measurementConverter() );

connect( mTopMarginSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::resizeMarginsChanged );
connect( mRightMarginSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::resizeMarginsChanged );
connect( mBottomMarginSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::resizeMarginsChanged );
connect( mLeftMarginSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPropertiesWidget::resizeMarginsChanged );
connect( mResizePageButton, &QPushButton::clicked, this, &QgsLayoutPropertiesWidget::resizeToContents );
}

void QgsLayoutPropertiesWidget::updateSnappingElements()
Expand Down Expand Up @@ -101,6 +128,28 @@ void QgsLayoutPropertiesWidget::snapToleranceChanged( int tolerance )
mLayout->snapper().setSnapTolerance( tolerance );
}

void QgsLayoutPropertiesWidget::resizeMarginsChanged()
{
mLayout->setCustomProperty( QStringLiteral( "resizeToContentsLeftMargin" ), mLeftMarginSpinBox->value() );
mLayout->setCustomProperty( QStringLiteral( "resizeToContentsTopMargin" ), mTopMarginSpinBox->value() );
mLayout->setCustomProperty( QStringLiteral( "resizeToContentsBottomMargin" ), mBottomMarginSpinBox->value() );
mLayout->setCustomProperty( QStringLiteral( "resizeToContentsRightMargin" ), mRightMarginSpinBox->value() );
mLayout->setCustomProperty( QStringLiteral( "imageCropMarginUnit" ), mMarginUnitsComboBox->unit() );
}

void QgsLayoutPropertiesWidget::resizeToContents()
{
mLayout->undoStack()->beginMacro( tr( "Resize to Contents" ) );

mLayout->pageCollection()->resizeToContents( QgsMargins( mLeftMarginSpinBox->value(),
mTopMarginSpinBox->value(),
mRightMarginSpinBox->value(),
mBottomMarginSpinBox->value() ),
mMarginUnitsComboBox->unit() );

mLayout->undoStack()->endMacro();
}

void QgsLayoutPropertiesWidget::blockSignals( bool block )
{
mGridResolutionSpinBox->blockSignals( block );
Expand Down
2 changes: 2 additions & 0 deletions src/app/layout/qgslayoutpropertieswidget.h
Expand Up @@ -36,6 +36,8 @@ class QgsLayoutPropertiesWidget: public QgsPanelWidget, private Ui::QgsLayoutWid
void gridOffsetYChanged( double d );
void gridOffsetUnitsChanged( QgsUnitTypes::LayoutUnit unit );
void snapToleranceChanged( int tolerance );
void resizeMarginsChanged();
void resizeToContents();

private:

Expand Down
71 changes: 71 additions & 0 deletions src/core/layout/qgslayoutpagecollection.cpp
Expand Up @@ -199,6 +199,77 @@ double QgsLayoutPageCollection::pageShadowWidth() const
return spaceBetweenPages() / 2;
}

void QgsLayoutPageCollection::resizeToContents( const QgsMargins &margins, QgsUnitTypes::LayoutUnit marginUnits )
{
if ( !mBlockUndoCommands )
mLayout->undoStack()->beginCommand( this, tr( "Resize to Contents" ) );

//calculate current bounds
QRectF bounds = mLayout->layoutBounds( true, 0.0 );

for ( int page = mPages.count() - 1; page > 0; page-- )
{
deletePage( page );
}

if ( mPages.empty() )
{
std::unique_ptr< QgsLayoutItemPage > page = qgis::make_unique< QgsLayoutItemPage >( mLayout );
addPage( page.release() );
}

QgsLayoutItemPage *page = mPages.at( 0 );

double marginLeft = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.left(), marginUnits ) );
double marginTop = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.top(), marginUnits ) );
double marginBottom = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.bottom(), marginUnits ) );
double marginRight = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.right(), marginUnits ) );

bounds.setWidth( bounds.width() + marginLeft + marginRight );
bounds.setHeight( bounds.height() + marginTop + marginBottom );

QgsLayoutSize newPageSize = mLayout->convertFromLayoutUnits( bounds.size(), mLayout->units() );
page->setPageSize( newPageSize );

reflow();

//also move all items so that top-left of bounds is at marginLeft, marginTop
double diffX = marginLeft - bounds.left();
double diffY = marginTop - bounds.top();

const QList<QGraphicsItem *> itemList = mLayout->items();
for ( QGraphicsItem *item : itemList )
{
if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
{
QgsLayoutItemPage *pageItem = dynamic_cast<QgsLayoutItemPage *>( layoutItem );
if ( !pageItem )
{
layoutItem->beginCommand( tr( "Move Item" ) );
layoutItem->attemptMoveBy( diffX, diffY );
layoutItem->endCommand();
}
}
}

//also move guides
mLayout->undoStack()->beginCommand( &mLayout->guides(), tr( "Move Guides" ) );
const QList< QgsLayoutGuide * > verticalGuides = mLayout->guides().guides( Qt::Vertical );
for ( QgsLayoutGuide *guide : verticalGuides )
{
guide->setLayoutPosition( guide->layoutPosition() + diffX );
}
const QList< QgsLayoutGuide * > horizontalGuides = mLayout->guides().guides( Qt::Horizontal );
for ( QgsLayoutGuide *guide : horizontalGuides )
{
guide->setLayoutPosition( guide->layoutPosition() + diffY );
}
mLayout->undoStack()->endCommand();

if ( !mBlockUndoCommands )
mLayout->undoStack()->endCommand();
}

bool QgsLayoutPageCollection::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const
{
QDomElement element = document.createElement( QStringLiteral( "PageCollection" ) );
Expand Down
10 changes: 10 additions & 0 deletions src/core/layout/qgslayoutpagecollection.h
Expand Up @@ -306,6 +306,16 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject, public QgsLayoutSeri
*/
double pageShadowWidth() const;

/**
* Resizes the layout to a single page which fits the current contents of the layout.
*
* Calling this method resets the number of pages to 1, with the size set to the
* minimum size required to fit all existing layout items. Items will also be
* repositioned so that the new top-left bounds of the layout is at the point
* (marginLeft, marginTop). An optional margin can be specified.
*/
void resizeToContents( const QgsMargins &margins, QgsUnitTypes::LayoutUnit marginUnits );

/**
* Stores the collection's state in a DOM element. The \a parentElement should refer to the parent layout's DOM element.
* \see readXml()
Expand Down
146 changes: 136 additions & 10 deletions src/ui/layout/qgslayoutwidgetbase.ui
Expand Up @@ -175,6 +175,126 @@
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="groupBox_5">
<property name="title">
<string>Resize layout to content</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Margin units</string>
</property>
</widget>
</item>
<item>
<widget class="QgsLayoutUnitsComboBox" name="mMarginUnitsComboBox"/>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Top margin</string>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QgsDoubleSpinBox" name="mTopMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item>
<widget class="QgsDoubleSpinBox" name="mLeftMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
<item>
<widget class="QgsDoubleSpinBox" name="mRightMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Bottom</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QgsDoubleSpinBox" name="mBottomMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QPushButton" name="mResizePageButton">
<property name="text">
<string>Resize layout</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
Expand All @@ -196,16 +316,6 @@
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsLayoutUnitsComboBox</class>
<extends>QComboBox</extends>
<header>qgslayoutunitscombobox.h</header>
</customwidget>
<customwidget>
<class>QgsScrollArea</class>
<extends>QScrollArea</extends>
Expand All @@ -218,6 +328,16 @@
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsLayoutUnitsComboBox</class>
<extends>QComboBox</extends>
<header>qgslayoutunitscombobox.h</header>
</customwidget>
<customwidget>
<class>QgsSpinBox</class>
<extends>QSpinBox</extends>
Expand All @@ -233,6 +353,12 @@
<tabstop>mOffsetYSpinBox</tabstop>
<tabstop>mGridOffsetUnitsComboBox</tabstop>
<tabstop>mSnapToleranceSpinBox</tabstop>
<tabstop>mMarginUnitsComboBox</tabstop>
<tabstop>mTopMarginSpinBox</tabstop>
<tabstop>mLeftMarginSpinBox</tabstop>
<tabstop>mRightMarginSpinBox</tabstop>
<tabstop>mBottomMarginSpinBox</tabstop>
<tabstop>mResizePageButton</tabstop>
</tabstops>
<resources/>
<connections/>
Expand Down

0 comments on commit fe5bd47

Please sign in to comment.