Skip to content
Permalink
Browse files

Start on reflow support for page collections (needs tests)

  • Loading branch information
nyalldawson committed Jul 25, 2017
1 parent 79a4694 commit 39bf23a5d58eee5454251d25c8fc10f2f3fffcd7
@@ -29,6 +29,16 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator
QgsLayout( QgsProject *project );
%Docstring
Construct a new layout linked to the specified ``project``.

If the layout is a "new" layout (as opposed to a layout which will
restore a previous state from XML) then initializeDefaults() should be
called on the new layout.
%End

void initializeDefaults();
%Docstring
Initializes an empty layout, e.g. by adding a default page to the layout. This should be called after creating
a new layout.
%End

QgsProject *project() const;
@@ -191,6 +201,29 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator
:rtype: QgsLayoutPageCollection
%End

QRectF layoutBounds( bool ignorePages = false, double margin = 0.0 ) const;
%Docstring
Calculates the bounds of all non-gui items in the layout. Ignores snap lines, mouse handles
and other cosmetic items.
\param ignorePages set to true to ignore page items
\param margin optional marginal (in percent, e.g., 0.05 = 5% ) to add around items
:return: layout bounds, in layout units.
:rtype: QRectF
%End

void addLayoutItem( QgsLayoutItem *item /Transfer/ );
%Docstring
Adds an ``item`` to the layout. This should be called instead of the base class addItem()
method. Ownership of the item is transferred to the layout.
%End

public slots:

void updateBounds();
%Docstring
Updates the scene bounds of the layout.
%End

signals:

void variablesChanged();
@@ -65,6 +65,9 @@ class QgsLayoutPageCollection : QObject
to the collection, and the page will automatically be added to the collection's
layout() (there is no need to manually add the page item to the layout).
The page will be added after all pages currently contained in the collection.

Calling addPage() automatically triggers a reflow() of pages.

.. seealso:: insertPage()
%End

@@ -80,6 +83,8 @@ class QgsLayoutPageCollection : QObject
(Page numbers in collections begin at 0 - so a ``beforePage`` of 0 will insert
the page before all existing pages).

Calling insertPage() automatically triggers a reflow() of pages.

.. seealso:: addPage()
%End

@@ -90,6 +95,8 @@ class QgsLayoutPageCollection : QObject

Page numbers in collections begin at 0 - so a ``pageNumber`` of 0 will delete
the first page in the collection.

Calling deletePage() automatically triggers a reflow() of pages.
%End

void setPageStyleSymbol( QgsFillSymbol *symbol );
@@ -107,6 +114,19 @@ class QgsLayoutPageCollection : QObject
:rtype: QgsFillSymbol
%End

void reflow();
%Docstring
Forces the page collection to reflow the arrangement of pages, e.g. to account
for page size/orientation change.
%End

double maximumPageWidth() const;
%Docstring
Returns the maximum width of pages in the collection. The returned value is
in layout units.
:rtype: float
%End

};

/************************************************************************
@@ -25,6 +25,14 @@ QgsLayout::QgsLayout( QgsProject *project )
setBackgroundBrush( QColor( 215, 215, 215 ) );
}

void QgsLayout::initializeDefaults()
{
// default to a A4 landscape page
QgsLayoutItemPage *page = new QgsLayoutItemPage( this );
page->setPageSize( QgsLayoutSize( 297, 210, QgsUnitTypes::LayoutMillimeters ) );
mPageCollection->addPage( page );
}

QgsProject *QgsLayout::project() const
{
return mProject;
@@ -112,3 +120,48 @@ QgsLayoutPageCollection *QgsLayout::pageCollection()
{
return mPageCollection.get();
}

QRectF QgsLayout::layoutBounds( bool ignorePages, double margin ) const
{
//start with an empty rectangle
QRectF bounds;

//add all QgsComposerItems and QgsPaperItems which are in the composition
Q_FOREACH ( const QGraphicsItem *item, items() )
{
const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
if ( !layoutItem )
continue;

bool isPage = layoutItem->type() == QgsLayoutItemRegistry::LayoutPage;
if ( !isPage || !ignorePages )
{
//expand bounds with current item's bounds
if ( bounds.isValid() )
bounds = bounds.united( item->sceneBoundingRect() );
else
bounds = item->sceneBoundingRect();
}
}

if ( bounds.isValid() && margin > 0.0 )
{
//finally, expand bounds out by specified margin of page size
double maxWidth = mPageCollection->maximumPageWidth();
bounds.adjust( -maxWidth * margin, -maxWidth * margin, maxWidth * margin, maxWidth * margin );
}

return bounds;

}

void QgsLayout::addLayoutItem( QgsLayoutItem *item )
{
addItem( item );
updateBounds();
}

void QgsLayout::updateBounds()
{
setSceneRect( layoutBounds( false, 0.05 ) );
}
@@ -45,9 +45,19 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext

/**
* Construct a new layout linked to the specified \a project.
*
* If the layout is a "new" layout (as opposed to a layout which will
* restore a previous state from XML) then initializeDefaults() should be
* called on the new layout.
*/
QgsLayout( QgsProject *project );

/**
* Initializes an empty layout, e.g. by adding a default page to the layout. This should be called after creating
* a new layout.
*/
void initializeDefaults();

/**
* The project associated with the layout. Used to get access to layers, map themes,
* relations and various other bits. It is never null.
@@ -211,6 +221,28 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
*/
QgsLayoutPageCollection *pageCollection();

/**
* Calculates the bounds of all non-gui items in the layout. Ignores snap lines, mouse handles
* and other cosmetic items.
* \param ignorePages set to true to ignore page items
* \param margin optional marginal (in percent, e.g., 0.05 = 5% ) to add around items
* \returns layout bounds, in layout units.
*/
QRectF layoutBounds( bool ignorePages = false, double margin = 0.0 ) const;

/**
* Adds an \a item to the layout. This should be called instead of the base class addItem()
* method. Ownership of the item is transferred to the layout.
*/
void addLayoutItem( QgsLayoutItem *item SIP_TRANSFER );

public slots:

/**
* Updates the scene bounds of the layout.
*/
void updateBounds();

signals:

/**
@@ -17,6 +17,8 @@
#include "qgslayoutpagecollection.h"
#include "qgslayout.h"

#define SPACE_BETWEEN_PAGES 10

QgsLayoutPageCollection::QgsLayoutPageCollection( QgsLayout *layout )
: QObject( layout )
, mLayout( layout )
@@ -41,6 +43,29 @@ void QgsLayoutPageCollection::setPageStyleSymbol( QgsFillSymbol *symbol )
mPageStyleSymbol.reset( static_cast<QgsFillSymbol *>( symbol->clone() ) );
}

void QgsLayoutPageCollection::reflow()
{
double currentY = 0;
QgsLayoutPoint p( 0, 0, mLayout->units() );
Q_FOREACH ( QgsLayoutItemPage *page, mPages )
{
page->attemptMove( p );
currentY += mLayout->convertToLayoutUnits( page->pageSize() ).height() + SPACE_BETWEEN_PAGES;
p.setY( currentY );
}
mLayout->updateBounds();
}

double QgsLayoutPageCollection::maximumPageWidth() const
{
double maxWidth = 0;
Q_FOREACH ( QgsLayoutItemPage *page, mPages )
{
maxWidth = qMax( maxWidth, mLayout->convertToLayoutUnits( page->pageSize() ).width() );
}
return maxWidth;
}

QgsLayout *QgsLayoutPageCollection::layout() const
{
return mLayout;
@@ -65,6 +90,7 @@ void QgsLayoutPageCollection::addPage( QgsLayoutItemPage *page )
{
mPages.append( page );
mLayout->addItem( page );
reflow();
}

void QgsLayoutPageCollection::insertPage( QgsLayoutItemPage *page, int beforePage )
@@ -81,6 +107,7 @@ void QgsLayoutPageCollection::insertPage( QgsLayoutItemPage *page, int beforePag
mPages.insert( beforePage, page );
}
mLayout->addItem( page );
reflow();
}

void QgsLayoutPageCollection::deletePage( int pageNumber )
@@ -91,6 +118,7 @@ void QgsLayoutPageCollection::deletePage( int pageNumber )
QgsLayoutItemPage *page = mPages.takeAt( pageNumber );
mLayout->removeItem( page );
page->deleteLater();
reflow();
}

void QgsLayoutPageCollection::createDefaultPageStyleSymbol()
@@ -78,6 +78,9 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
* to the collection, and the page will automatically be added to the collection's
* layout() (there is no need to manually add the page item to the layout).
* The page will be added after all pages currently contained in the collection.
*
* Calling addPage() automatically triggers a reflow() of pages.
*
* \see insertPage()
*/
void addPage( QgsLayoutItemPage *page SIP_TRANSFER );
@@ -93,6 +96,8 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
* (Page numbers in collections begin at 0 - so a \a beforePage of 0 will insert
* the page before all existing pages).
*
* Calling insertPage() automatically triggers a reflow() of pages.
*
* \see addPage()
*/
void insertPage( QgsLayoutItemPage *page SIP_TRANSFER, int beforePage );
@@ -103,6 +108,8 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
*
* Page numbers in collections begin at 0 - so a \a pageNumber of 0 will delete
* the first page in the collection.
*
* Calling deletePage() automatically triggers a reflow() of pages.
*/
void deletePage( int pageNumber );

@@ -120,6 +127,18 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
*/
const QgsFillSymbol *pageStyleSymbol() const { return mPageStyleSymbol.get(); }

/**
* Forces the page collection to reflow the arrangement of pages, e.g. to account
* for page size/orientation change.
*/
void reflow();

/**
* Returns the maximum width of pages in the collection. The returned value is
* in layout units.
*/
double maximumPageWidth() const;

private:

QgsLayout *mLayout = nullptr;
@@ -115,7 +115,7 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even
settings.setValue( QStringLiteral( "LayoutDesigner/lastItemHeight" ), item->sizeWithUnits().height() );
settings.setValue( QStringLiteral( "LayoutDesigner/lastSizeUnit" ), static_cast< int >( item->sizeWithUnits().units() ) );

layout()->addItem( item );
layout()->addLayoutItem( item );
}

void QgsLayoutViewToolAddItem::deactivate()

0 comments on commit 39bf23a

Please sign in to comment.
You can’t perform that action at this time.