Skip to content

Commit 39bf23a

Browse files
committed
Start on reflow support for page collections (needs tests)
1 parent 79a4694 commit 39bf23a

File tree

9 files changed

+389
-2
lines changed

9 files changed

+389
-2
lines changed

python/core/layout/qgslayout.sip

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator
2929
QgsLayout( QgsProject *project );
3030
%Docstring
3131
Construct a new layout linked to the specified ``project``.
32+
33+
If the layout is a "new" layout (as opposed to a layout which will
34+
restore a previous state from XML) then initializeDefaults() should be
35+
called on the new layout.
36+
%End
37+
38+
void initializeDefaults();
39+
%Docstring
40+
Initializes an empty layout, e.g. by adding a default page to the layout. This should be called after creating
41+
a new layout.
3242
%End
3343

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

204+
QRectF layoutBounds( bool ignorePages = false, double margin = 0.0 ) const;
205+
%Docstring
206+
Calculates the bounds of all non-gui items in the layout. Ignores snap lines, mouse handles
207+
and other cosmetic items.
208+
\param ignorePages set to true to ignore page items
209+
\param margin optional marginal (in percent, e.g., 0.05 = 5% ) to add around items
210+
:return: layout bounds, in layout units.
211+
:rtype: QRectF
212+
%End
213+
214+
void addLayoutItem( QgsLayoutItem *item /Transfer/ );
215+
%Docstring
216+
Adds an ``item`` to the layout. This should be called instead of the base class addItem()
217+
method. Ownership of the item is transferred to the layout.
218+
%End
219+
220+
public slots:
221+
222+
void updateBounds();
223+
%Docstring
224+
Updates the scene bounds of the layout.
225+
%End
226+
194227
signals:
195228

196229
void variablesChanged();

python/core/layout/qgslayoutpagecollection.sip

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ class QgsLayoutPageCollection : QObject
6565
to the collection, and the page will automatically be added to the collection's
6666
layout() (there is no need to manually add the page item to the layout).
6767
The page will be added after all pages currently contained in the collection.
68+
69+
Calling addPage() automatically triggers a reflow() of pages.
70+
6871
.. seealso:: insertPage()
6972
%End
7073

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

86+
Calling insertPage() automatically triggers a reflow() of pages.
87+
8388
.. seealso:: addPage()
8489
%End
8590

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

9196
Page numbers in collections begin at 0 - so a ``pageNumber`` of 0 will delete
9297
the first page in the collection.
98+
99+
Calling deletePage() automatically triggers a reflow() of pages.
93100
%End
94101

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

117+
void reflow();
118+
%Docstring
119+
Forces the page collection to reflow the arrangement of pages, e.g. to account
120+
for page size/orientation change.
121+
%End
122+
123+
double maximumPageWidth() const;
124+
%Docstring
125+
Returns the maximum width of pages in the collection. The returned value is
126+
in layout units.
127+
:rtype: float
128+
%End
129+
110130
};
111131

112132
/************************************************************************

src/core/layout/qgslayout.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ QgsLayout::QgsLayout( QgsProject *project )
2525
setBackgroundBrush( QColor( 215, 215, 215 ) );
2626
}
2727

28+
void QgsLayout::initializeDefaults()
29+
{
30+
// default to a A4 landscape page
31+
QgsLayoutItemPage *page = new QgsLayoutItemPage( this );
32+
page->setPageSize( QgsLayoutSize( 297, 210, QgsUnitTypes::LayoutMillimeters ) );
33+
mPageCollection->addPage( page );
34+
}
35+
2836
QgsProject *QgsLayout::project() const
2937
{
3038
return mProject;
@@ -112,3 +120,48 @@ QgsLayoutPageCollection *QgsLayout::pageCollection()
112120
{
113121
return mPageCollection.get();
114122
}
123+
124+
QRectF QgsLayout::layoutBounds( bool ignorePages, double margin ) const
125+
{
126+
//start with an empty rectangle
127+
QRectF bounds;
128+
129+
//add all QgsComposerItems and QgsPaperItems which are in the composition
130+
Q_FOREACH ( const QGraphicsItem *item, items() )
131+
{
132+
const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
133+
if ( !layoutItem )
134+
continue;
135+
136+
bool isPage = layoutItem->type() == QgsLayoutItemRegistry::LayoutPage;
137+
if ( !isPage || !ignorePages )
138+
{
139+
//expand bounds with current item's bounds
140+
if ( bounds.isValid() )
141+
bounds = bounds.united( item->sceneBoundingRect() );
142+
else
143+
bounds = item->sceneBoundingRect();
144+
}
145+
}
146+
147+
if ( bounds.isValid() && margin > 0.0 )
148+
{
149+
//finally, expand bounds out by specified margin of page size
150+
double maxWidth = mPageCollection->maximumPageWidth();
151+
bounds.adjust( -maxWidth * margin, -maxWidth * margin, maxWidth * margin, maxWidth * margin );
152+
}
153+
154+
return bounds;
155+
156+
}
157+
158+
void QgsLayout::addLayoutItem( QgsLayoutItem *item )
159+
{
160+
addItem( item );
161+
updateBounds();
162+
}
163+
164+
void QgsLayout::updateBounds()
165+
{
166+
setSceneRect( layoutBounds( false, 0.05 ) );
167+
}

src/core/layout/qgslayout.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,19 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
4545

4646
/**
4747
* Construct a new layout linked to the specified \a project.
48+
*
49+
* If the layout is a "new" layout (as opposed to a layout which will
50+
* restore a previous state from XML) then initializeDefaults() should be
51+
* called on the new layout.
4852
*/
4953
QgsLayout( QgsProject *project );
5054

55+
/**
56+
* Initializes an empty layout, e.g. by adding a default page to the layout. This should be called after creating
57+
* a new layout.
58+
*/
59+
void initializeDefaults();
60+
5161
/**
5262
* The project associated with the layout. Used to get access to layers, map themes,
5363
* relations and various other bits. It is never null.
@@ -211,6 +221,28 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
211221
*/
212222
QgsLayoutPageCollection *pageCollection();
213223

224+
/**
225+
* Calculates the bounds of all non-gui items in the layout. Ignores snap lines, mouse handles
226+
* and other cosmetic items.
227+
* \param ignorePages set to true to ignore page items
228+
* \param margin optional marginal (in percent, e.g., 0.05 = 5% ) to add around items
229+
* \returns layout bounds, in layout units.
230+
*/
231+
QRectF layoutBounds( bool ignorePages = false, double margin = 0.0 ) const;
232+
233+
/**
234+
* Adds an \a item to the layout. This should be called instead of the base class addItem()
235+
* method. Ownership of the item is transferred to the layout.
236+
*/
237+
void addLayoutItem( QgsLayoutItem *item SIP_TRANSFER );
238+
239+
public slots:
240+
241+
/**
242+
* Updates the scene bounds of the layout.
243+
*/
244+
void updateBounds();
245+
214246
signals:
215247

216248
/**

src/core/layout/qgslayoutpagecollection.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "qgslayoutpagecollection.h"
1818
#include "qgslayout.h"
1919

20+
#define SPACE_BETWEEN_PAGES 10
21+
2022
QgsLayoutPageCollection::QgsLayoutPageCollection( QgsLayout *layout )
2123
: QObject( layout )
2224
, mLayout( layout )
@@ -41,6 +43,29 @@ void QgsLayoutPageCollection::setPageStyleSymbol( QgsFillSymbol *symbol )
4143
mPageStyleSymbol.reset( static_cast<QgsFillSymbol *>( symbol->clone() ) );
4244
}
4345

46+
void QgsLayoutPageCollection::reflow()
47+
{
48+
double currentY = 0;
49+
QgsLayoutPoint p( 0, 0, mLayout->units() );
50+
Q_FOREACH ( QgsLayoutItemPage *page, mPages )
51+
{
52+
page->attemptMove( p );
53+
currentY += mLayout->convertToLayoutUnits( page->pageSize() ).height() + SPACE_BETWEEN_PAGES;
54+
p.setY( currentY );
55+
}
56+
mLayout->updateBounds();
57+
}
58+
59+
double QgsLayoutPageCollection::maximumPageWidth() const
60+
{
61+
double maxWidth = 0;
62+
Q_FOREACH ( QgsLayoutItemPage *page, mPages )
63+
{
64+
maxWidth = qMax( maxWidth, mLayout->convertToLayoutUnits( page->pageSize() ).width() );
65+
}
66+
return maxWidth;
67+
}
68+
4469
QgsLayout *QgsLayoutPageCollection::layout() const
4570
{
4671
return mLayout;
@@ -65,6 +90,7 @@ void QgsLayoutPageCollection::addPage( QgsLayoutItemPage *page )
6590
{
6691
mPages.append( page );
6792
mLayout->addItem( page );
93+
reflow();
6894
}
6995

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

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

96124
void QgsLayoutPageCollection::createDefaultPageStyleSymbol()

src/core/layout/qgslayoutpagecollection.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
7878
* to the collection, and the page will automatically be added to the collection's
7979
* layout() (there is no need to manually add the page item to the layout).
8080
* The page will be added after all pages currently contained in the collection.
81+
*
82+
* Calling addPage() automatically triggers a reflow() of pages.
83+
*
8184
* \see insertPage()
8285
*/
8386
void addPage( QgsLayoutItemPage *page SIP_TRANSFER );
@@ -93,6 +96,8 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
9396
* (Page numbers in collections begin at 0 - so a \a beforePage of 0 will insert
9497
* the page before all existing pages).
9598
*
99+
* Calling insertPage() automatically triggers a reflow() of pages.
100+
*
96101
* \see addPage()
97102
*/
98103
void insertPage( QgsLayoutItemPage *page SIP_TRANSFER, int beforePage );
@@ -103,6 +108,8 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
103108
*
104109
* Page numbers in collections begin at 0 - so a \a pageNumber of 0 will delete
105110
* the first page in the collection.
111+
*
112+
* Calling deletePage() automatically triggers a reflow() of pages.
106113
*/
107114
void deletePage( int pageNumber );
108115

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

130+
/**
131+
* Forces the page collection to reflow the arrangement of pages, e.g. to account
132+
* for page size/orientation change.
133+
*/
134+
void reflow();
135+
136+
/**
137+
* Returns the maximum width of pages in the collection. The returned value is
138+
* in layout units.
139+
*/
140+
double maximumPageWidth() const;
141+
123142
private:
124143

125144
QgsLayout *mLayout = nullptr;

src/gui/layout/qgslayoutviewtooladditem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even
115115
settings.setValue( QStringLiteral( "LayoutDesigner/lastItemHeight" ), item->sizeWithUnits().height() );
116116
settings.setValue( QStringLiteral( "LayoutDesigner/lastSizeUnit" ), static_cast< int >( item->sizeWithUnits().units() ) );
117117

118-
layout()->addItem( item );
118+
layout()->addLayoutItem( item );
119119
}
120120

121121
void QgsLayoutViewToolAddItem::deactivate()

0 commit comments

Comments
 (0)