1 /***************************************************************************
2 testqgslayoutitem.cpp
3 -----------------------
4 begin : June 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgslayoutitem.h"
19 #include "qgslayoutitemregistry.h"
20 #include "qgslayout.h"
21 #include "qgsmultirenderchecker.h"
22 #include "qgstest.h"
23 #include "qgsproject.h"
24 #include "qgsreadwritecontext.h"
25 #include "qgslayoutitemundocommand.h"
26 #include "qgslayoutitemmap.h"
27 #include "qgslayoutitemlabel.h"
28 #include "qgslayoutitemshape.h"
29 #include "qgslayouteffect.h"
30 #include "qgsfillsymbollayer.h"
31 #include "qgslayoutpagecollection.h"
32 #include "qgslayoutundostack.h"
33 #include "qgsvectorlayer.h"
34 #include "qgsexpressioncontextutils.h"
35 #include "qgsfillsymbol.h"
36
37 #include <QObject>
38 #include <QPainter>
39 #include <QImage>
40 #include <QtTest/QSignalSpy>
41
42
43 //simple item for testing, since some methods in QgsLayoutItem are pure virtual
44 class TestItem : public QgsLayoutItem
45 {
46 Q_OBJECT
47
48 public:
49
TestItem(QgsLayout * layout)50 TestItem( QgsLayout *layout ) : QgsLayoutItem( layout )
51 {
52 setFrameEnabled( false );
53 setBackgroundEnabled( false );
54 }
55
56 //implement pure virtual methods
type() const57 int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; }
58
59 bool forceResize = false;
60
61 protected:
draw(QgsLayoutItemRenderContext & context)62 void draw( QgsLayoutItemRenderContext &context ) override
63 {
64 QPainter *painter = context.renderContext().painter();
65 painter->save();
66 painter->setRenderHint( QPainter::Antialiasing, false );
67 painter->setPen( Qt::NoPen );
68 painter->setBrush( QColor( 255, 100, 100, 200 ) );
69 painter->drawRect( rect() );
70 painter->restore();
71 }
72
applyItemSizeConstraint(QSizeF targetSize)73 QSizeF applyItemSizeConstraint( QSizeF targetSize ) override
74 {
75 if ( !forceResize )
76 return targetSize;
77
78 return QSizeF( 17, 27 );
79 }
80 };
81
82 //item with minimum size
83 class MinSizedItem : public TestItem
84 {
85 Q_OBJECT
86
87 public:
MinSizedItem(QgsLayout * layout)88 MinSizedItem( QgsLayout *layout ) : TestItem( layout )
89 {
90 setMinimumSize( QgsLayoutSize( 5.0, 10.0, QgsUnitTypes::LayoutCentimeters ) );
91 }
92
updateMinSize(QgsLayoutSize size)93 void updateMinSize( QgsLayoutSize size )
94 {
95 setMinimumSize( size );
96 }
97
98 };
99
100 //item with fixed size
101 class FixedSizedItem : public TestItem
102 {
103 Q_OBJECT
104
105 public:
106
FixedSizedItem(QgsLayout * layout)107 FixedSizedItem( QgsLayout *layout ) : TestItem( layout )
108 {
109 setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutInches ) );
110 }
111
updateFixedSize(QgsLayoutSize size)112 void updateFixedSize( QgsLayoutSize size )
113 {
114 setFixedSize( size );
115 }
116 };
117
118 //item with both conflicting fixed and minimum size
119 class FixedMinSizedItem : public TestItem
120 {
121 Q_OBJECT
122
123 public:
124
FixedMinSizedItem(QgsLayout * layout)125 FixedMinSizedItem( QgsLayout *layout ) : TestItem( layout )
126 {
127 setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
128 setMinimumSize( QgsLayoutSize( 5.0, 9.0, QgsUnitTypes::LayoutCentimeters ) );
129 }
130 };
131
132
133 class TestQgsLayoutItem: public QObject
134 {
135 Q_OBJECT
136
137 private slots:
138 void initTestCase();// will be called before the first testfunction is executed.
139 void cleanupTestCase();// will be called after the last testfunction was executed.
140 void init();// will be called before each testfunction is executed.
141 void cleanup();// will be called after every testfunction.
142 void creation(); //test creation of QgsLayoutItem
143 void uuid();
144 void id();
145 void registry();
146 void shouldDrawDebug();
147 void shouldDrawAntialiased();
148 void preparePainter();
149 void debugRect();
150 void draw();
151 void resize();
152 void referencePoint();
153 void itemPositionReferencePoint();
154 void adjustPointForReference();
155 void positionAtReferencePoint();
156 void fixedSize();
157 void minSize();
158 void move();
159 void positionWithUnits();
160 void sizeWithUnits();
161 void dataDefinedPosition();
162 void dataDefinedSize();
163 void combinedDataDefinedPositionAndSize();
164 void rotation();
165 void writeXml();
166 void readXml();
167 void writeReadXmlProperties();
168 void undoRedo();
169 void multiItemUndo();
170 void overlappingUndo();
171 void blendMode();
172 void opacity();
173 void excludeFromExports();
174 void setSceneRect();
175 void page();
176 void itemVariablesFunction();
177 void variables();
178 void mapCreditsFunction();
179
180 private:
181
182 QString mReport;
183
184 bool renderCheck( QString testName, QImage &image, int mismatchCount );
185
186 std::unique_ptr< QgsLayoutItem > createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original );
187
188 };
189
initTestCase()190 void TestQgsLayoutItem::initTestCase()
191 {
192 mReport = QStringLiteral( "<h1>Layout Item Tests</h1>\n" );
193 }
194
cleanupTestCase()195 void TestQgsLayoutItem::cleanupTestCase()
196 {
197 const QString myReportFile = QDir::tempPath() + QDir::separator() + "qgistest.html";
198 QFile myFile( myReportFile );
199 if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
200 {
201 QTextStream myQTextStream( &myFile );
202 myQTextStream << mReport;
203 myFile.close();
204 }
205 }
206
init()207 void TestQgsLayoutItem::init()
208 {
209
210 }
211
cleanup()212 void TestQgsLayoutItem::cleanup()
213 {
214
215 }
216
creation()217 void TestQgsLayoutItem::creation()
218 {
219 QgsProject p;
220 QgsLayout *layout = new QgsLayout( &p );
221 TestItem *item = new TestItem( layout );
222 QVERIFY( item );
223 delete item;
224 delete layout;
225 }
226
uuid()227 void TestQgsLayoutItem::uuid()
228 {
229 QgsProject p;
230 QgsLayout l( &p );
231
232 //basic test of uuid
233 const TestItem item( &l );
234 const TestItem item2( &l );
235 QVERIFY( item.uuid() != item2.uuid() );
236 }
237
id()238 void TestQgsLayoutItem::id()
239 {
240 QgsProject p;
241 QgsLayout l( &p );
242 TestItem item( &l );
243 item.setId( QStringLiteral( "test" ) );
244 QCOMPARE( item.id(), QStringLiteral( "test" ) );
245 }
246
registry()247 void TestQgsLayoutItem::registry()
248 {
249 // test QgsLayoutItemRegistry
250 QgsLayoutItemRegistry registry;
251
252 // empty registry
253 QVERIFY( !registry.itemMetadata( -1 ) );
254 QVERIFY( registry.itemTypes().isEmpty() );
255 QVERIFY( !registry.createItem( 1, nullptr ) );
256
257 auto create = []( QgsLayout * layout )->QgsLayoutItem *
258 {
259 return new TestItem( layout );
260 };
261 auto resolve = []( QVariantMap & props, const QgsPathResolver &, bool )
262 {
263 props.clear();
264 };
265
266 const QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemRegistry::typeAdded );
267
268 QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), QStringLiteral( "my types" ), create, resolve );
269 QVERIFY( registry.addLayoutItemType( metadata ) );
270 QCOMPARE( spyTypeAdded.count(), 1 );
271 QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), 2 );
272 QCOMPARE( spyTypeAdded.value( 0 ).at( 1 ).toString(), QStringLiteral( "my type" ) );
273 // duplicate type id
274 QVERIFY( !registry.addLayoutItemType( metadata ) );
275 QCOMPARE( spyTypeAdded.count(), 1 );
276
277 //retrieve metadata
278 QVERIFY( !registry.itemMetadata( -1 ) );
279 QCOMPARE( registry.itemMetadata( 2 )->visibleName(), QStringLiteral( "my type" ) );
280 QCOMPARE( registry.itemMetadata( 2 )->visiblePluralName(), QStringLiteral( "my types" ) );
281 QCOMPARE( registry.itemTypes().count(), 1 );
282 QCOMPARE( registry.itemTypes().value( 2 ), QStringLiteral( "my type" ) );
283 QgsLayoutItem *item = registry.createItem( 2, nullptr );
284 QVERIFY( item );
285 QVERIFY( dynamic_cast< TestItem *>( item ) );
286 delete item;
287 QVariantMap props;
288 props.insert( QStringLiteral( "a" ), 5 );
289 registry.resolvePaths( 1, props, QgsPathResolver(), true );
290 QCOMPARE( props.size(), 1 );
291 registry.resolvePaths( 2, props, QgsPathResolver(), true );
292 QVERIFY( props.isEmpty() );
293
294 //test populate
295 QgsLayoutItemRegistry reg2;
296 QVERIFY( reg2.itemTypes().isEmpty() );
297 QVERIFY( reg2.populate() );
298 QVERIFY( !reg2.itemTypes().isEmpty() );
299 QVERIFY( !reg2.populate() );
300 }
301
shouldDrawDebug()302 void TestQgsLayoutItem::shouldDrawDebug()
303 {
304 QgsProject p;
305 QgsLayout l( &p );
306 TestItem *item = new TestItem( &l );
307 l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true );
308 QVERIFY( item->shouldDrawDebugRect() );
309 l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, false );
310 QVERIFY( !item->shouldDrawDebugRect() );
311 delete item;
312 }
313
shouldDrawAntialiased()314 void TestQgsLayoutItem::shouldDrawAntialiased()
315 {
316 QgsProject p;
317 QgsLayout l( &p );
318 TestItem *item = new TestItem( &l );
319 l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false );
320 QVERIFY( !item->shouldDrawAntialiased() );
321 l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, true );
322 QVERIFY( item->shouldDrawAntialiased() );
323 delete item;
324 }
325
preparePainter()326 void TestQgsLayoutItem::preparePainter()
327 {
328 QgsProject p;
329 QgsLayout l( &p );
330 TestItem *item = new TestItem( &l );
331 //test with no painter
332 item->preparePainter( nullptr );
333
334 //test antialiasing correctly set for painter
335 QImage image( QSize( 100, 100 ), QImage::Format_ARGB32 );
336 QPainter painter;
337 painter.begin( &image );
338 l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false );
339 item->preparePainter( &painter );
340 QVERIFY( !( painter.renderHints() & QPainter::Antialiasing ) );
341 l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, true );
342 item->preparePainter( &painter );
343 QVERIFY( painter.renderHints() & QPainter::Antialiasing );
344 delete item;
345 }
346
debugRect()347 void TestQgsLayoutItem::debugRect()
348 {
349 QgsProject p;
350 QgsLayout l( &p );
351 TestItem *item = new TestItem( &l );
352 l.addItem( item );
353 item->setPos( 100, 100 );
354 item->setRect( 0, 0, 200, 200 );
355 l.setSceneRect( 0, 0, 400, 400 );
356 l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true );
357 QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 );
358 image.fill( 0 );
359 QPainter painter( &image );
360 l.render( &painter );
361 painter.end();
362
363 const bool result = renderCheck( QStringLiteral( "layoutitem_debugrect" ), image, 0 );
364 QVERIFY( result );
365 }
366
draw()367 void TestQgsLayoutItem::draw()
368 {
369 QgsProject p;
370 QgsLayout l( &p );
371 TestItem *item = new TestItem( &l );
372 l.addItem( item );
373 item->setPos( 100, 100 );
374 item->setRect( 0, 0, 200, 200 );
375 l.setSceneRect( 0, 0, 400, 400 );
376 l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false ); //disable antialiasing to limit cross platform differences
377 QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 );
378 image.fill( 0 );
379 QPainter painter( &image );
380 l.render( &painter );
381 painter.end();
382 const bool result = renderCheck( QStringLiteral( "layoutitem_draw" ), image, 0 );
383 QVERIFY( result );
384 }
385
renderCheck(QString testName,QImage & image,int mismatchCount)386 bool TestQgsLayoutItem::renderCheck( QString testName, QImage &image, int mismatchCount )
387 {
388 mReport += "<h2>" + testName + "</h2>\n";
389 const QString myTmpDir = QDir::tempPath() + QDir::separator();
390 const QString myFileName = myTmpDir + testName + ".png";
391 image.save( myFileName, "PNG" );
392 QgsRenderChecker myChecker;
393 myChecker.setControlPathPrefix( QStringLiteral( "layouts" ) );
394 myChecker.setControlName( "expected_" + testName );
395 myChecker.setRenderedImage( myFileName );
396 const bool myResultFlag = myChecker.compareImages( testName, mismatchCount );
397 mReport += myChecker.report();
398 return myResultFlag;
399 }
400
positionWithUnits()401 void TestQgsLayoutItem::positionWithUnits()
402 {
403 QgsProject p;
404 QgsLayout l( &p );
405
406 std::unique_ptr< TestItem > item( new TestItem( &l ) );
407 item->attemptMove( QgsLayoutPoint( 60.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
408 QCOMPARE( item->positionWithUnits().x(), 60.0 );
409 QCOMPARE( item->positionWithUnits().y(), 15.0 );
410 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
411 item->attemptMove( QgsLayoutPoint( 50.0, 100.0, QgsUnitTypes::LayoutPixels ) );
412 QCOMPARE( item->positionWithUnits().x(), 50.0 );
413 QCOMPARE( item->positionWithUnits().y(), 100.0 );
414 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutPixels );
415 }
416
sizeWithUnits()417 void TestQgsLayoutItem::sizeWithUnits()
418 {
419 QgsProject p;
420 QgsLayout l( &p );
421
422 TestItem *item = new TestItem( &l );
423 item->attemptResize( QgsLayoutSize( 60.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
424 QCOMPARE( item->sizeWithUnits().width(), 60.0 );
425 QCOMPARE( item->sizeWithUnits().height(), 15.0 );
426 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
427 item->attemptResize( QgsLayoutSize( 50.0, 100.0, QgsUnitTypes::LayoutPixels ) );
428 QCOMPARE( item->sizeWithUnits().width(), 50.0 );
429 QCOMPARE( item->sizeWithUnits().height(), 100.0 );
430 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutPixels );
431
432 delete item;
433 }
434
dataDefinedPosition()435 void TestQgsLayoutItem::dataDefinedPosition()
436 {
437 QgsProject p;
438 QgsLayout l( &p );
439
440 //test setting data defined position
441 TestItem *item = new TestItem( &l );
442 l.setUnits( QgsUnitTypes::LayoutMillimeters );
443 item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
444 item->attemptResize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
445
446 // position x
447 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+7" ) ) );
448 item->refreshDataDefinedProperty( QgsLayoutObject::PositionX );
449 QCOMPARE( item->positionWithUnits().x(), 11.0 );
450 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
451 QCOMPARE( item->scenePos().x(), 110.0 ); //mm
452
453 //position y
454 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+3" ) ) );
455 item->refreshDataDefinedProperty( QgsLayoutObject::PositionY );
456 QCOMPARE( item->positionWithUnits().y(), 5.0 );
457 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
458 QCOMPARE( item->scenePos().y(), 50.0 ); //mm
459
460 //refreshPosition should also respect data defined positioning
461 item->setPos( 0, 0 );
462 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
463 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
464 item->refreshItemPosition();
465 QCOMPARE( item->positionWithUnits().x(), 12.0 );
466 QCOMPARE( item->positionWithUnits().y(), 6.0 );
467 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
468 QCOMPARE( item->scenePos().x(), 120.0 ); //mm
469 QCOMPARE( item->scenePos().y(), 60.0 ); //mm
470
471 //also check that data defined position overrides when attempting to move
472 item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
473 QCOMPARE( item->positionWithUnits().x(), 12.0 );
474 QCOMPARE( item->positionWithUnits().y(), 6.0 );
475 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
476 QCOMPARE( item->scenePos().x(), 120.0 ); //mm
477 QCOMPARE( item->scenePos().y(), 60.0 ); //mm
478 //restriction only for x position
479 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
480 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty() );
481 item->attemptMove( QgsLayoutPoint( 6.0, 1.5, QgsUnitTypes::LayoutCentimeters ) );
482 QCOMPARE( item->positionWithUnits().x(), 12.0 );
483 QCOMPARE( item->positionWithUnits().y(), 1.5 );
484 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
485 QCOMPARE( item->scenePos().x(), 120.0 ); //mm
486 QCOMPARE( item->scenePos().y(), 15.0 ); //mm
487 //restriction only for y position
488 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty() );
489 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
490 item->attemptMove( QgsLayoutPoint( 7.0, 1.5, QgsUnitTypes::LayoutCentimeters ) );
491 QCOMPARE( item->positionWithUnits().x(), 7.0 );
492 QCOMPARE( item->positionWithUnits().y(), 6.0 );
493 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
494 QCOMPARE( item->scenePos().x(), 70.0 ); //mm
495 QCOMPARE( item->scenePos().y(), 60.0 ); //mm
496
497 //check change of units should apply to data defined position
498 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
499 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
500 //first set to same as existing position, but with different units
501 item->attemptMove( QgsLayoutPoint( 120.0, 60.0, QgsUnitTypes::LayoutMillimeters ) );
502 //data defined position should utilize new units
503 QCOMPARE( item->positionWithUnits().x(), 12.0 ); //mm
504 QCOMPARE( item->positionWithUnits().y(), 6.0 ); //mm
505 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
506 QCOMPARE( item->scenePos().x(), 12.0 ); //mm
507 QCOMPARE( item->scenePos().y(), 6.0 ); //mm
508
509 //test that data defined position applies to item's reference point
510 item->attemptMove( QgsLayoutPoint( 12.0, 6.0, QgsUnitTypes::LayoutCentimeters ) );
511 item->setReferencePoint( QgsLayoutItem::LowerRight );
512 QCOMPARE( item->positionWithUnits().x(), 12.0 ); //cm
513 QCOMPARE( item->positionWithUnits().y(), 6.0 ); //cm
514 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
515 QCOMPARE( item->scenePos().x(), 100.0 ); //mm
516 QCOMPARE( item->scenePos().y(), 20.0 ); //mm
517
518 //also check setting data defined position AFTER setting reference point
519 item->setPos( 0, 0 );
520 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "6+10" ) ) );
521 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+6" ) ) );
522 item->refreshItemPosition();
523 QCOMPARE( item->positionWithUnits().x(), 16.0 ); //cm
524 QCOMPARE( item->positionWithUnits().y(), 8.0 ); //cm
525 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
526 QCOMPARE( item->scenePos().x(), 140.0 ); //mm
527 QCOMPARE( item->scenePos().y(), 40.0 ); //mm
528
529 delete item;
530 }
531
dataDefinedSize()532 void TestQgsLayoutItem::dataDefinedSize()
533 {
534 QgsProject p;
535 QgsLayout l( &p );
536
537 //test setting data defined size
538 TestItem *item = new TestItem( &l );
539 l.setUnits( QgsUnitTypes::LayoutMillimeters );
540 item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
541 item->attemptResize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
542
543 //width
544 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+7" ) ) );
545 item->refreshDataDefinedProperty( QgsLayoutObject::ItemWidth );
546 QCOMPARE( item->sizeWithUnits().width(), 11.0 );
547 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
548 QCOMPARE( item->rect().width(), 110.0 ); //mm
549
550 //height
551 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+3" ) ) );
552 item->refreshDataDefinedProperty( QgsLayoutObject::ItemHeight );
553 QCOMPARE( item->sizeWithUnits().height(), 5.0 );
554 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
555 QCOMPARE( item->rect().height(), 50.0 ); //mm
556
557 //refreshSize should also respect data defined size
558 item->setRect( 0.0, 0.0, 9.0, 8.0 );
559 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
560 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
561 item->refreshItemSize();
562 QCOMPARE( item->sizeWithUnits().width(), 12.0 );
563 QCOMPARE( item->sizeWithUnits().height(), 6.0 );
564 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
565 QCOMPARE( item->rect().width(), 120.0 ); //mm
566 QCOMPARE( item->rect().height(), 60.0 ); //mm
567
568 //also check that data defined size overrides when attempting to resize
569 item->attemptResize( QgsLayoutSize( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
570 QCOMPARE( item->sizeWithUnits().width(), 12.0 );
571 QCOMPARE( item->sizeWithUnits().height(), 6.0 );
572 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
573 QCOMPARE( item->rect().width(), 120.0 ); //mm
574 QCOMPARE( item->rect().height(), 60.0 ); //mm
575 //restriction only for width
576 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
577 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
578 item->attemptResize( QgsLayoutSize( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
579 QCOMPARE( item->sizeWithUnits().width(), 12.0 );
580 QCOMPARE( item->sizeWithUnits().height(), 1.5 );
581 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
582 QCOMPARE( item->rect().width(), 120.0 ); //mm
583 QCOMPARE( item->rect().height(), 15.0 ); //mm
584 //restriction only for y position
585 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
586 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
587 item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
588 QCOMPARE( item->sizeWithUnits().width(), 7.0 );
589 QCOMPARE( item->sizeWithUnits().height(), 6.0 );
590 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
591 QCOMPARE( item->rect().width(), 70.0 ); //mm
592 QCOMPARE( item->rect().height(), 60.0 ); //mm
593
594 // data defined page size
595 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
596 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
597 item->dataDefinedProperties().setProperty( QgsLayoutObject::PresetPaperSize, QgsProperty::fromValue( QStringLiteral( "A5" ) ) );
598 item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
599 QCOMPARE( item->sizeWithUnits().width(), 14.8 );
600 QCOMPARE( item->sizeWithUnits().height(), 21.0 );
601 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
602 QCOMPARE( item->rect().width(), 148.0 ); //mm
603 QCOMPARE( item->rect().height(), 210.0 ); //mm
604 // data defined height/width should override page size
605 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromValue( "13.0" ) );
606 item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
607 QCOMPARE( item->sizeWithUnits().width(), 13.0 );
608 QCOMPARE( item->sizeWithUnits().height(), 21.0 );
609 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
610 QCOMPARE( item->rect().width(), 130.0 ); //mm
611 QCOMPARE( item->rect().height(), 210.0 ); //mm
612 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromValue( "3.0" ) );
613 item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
614 QCOMPARE( item->sizeWithUnits().width(), 13.0 );
615 QCOMPARE( item->sizeWithUnits().height(), 3.0 );
616 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
617 QCOMPARE( item->rect().width(), 130.0 ); //mm
618 QCOMPARE( item->rect().height(), 30.0 ); //mm
619 // data defined orientation
620 item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty::fromValue( "portrait" ) );
621 item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
622 QCOMPARE( item->sizeWithUnits().width(), 3.0 );
623 QCOMPARE( item->sizeWithUnits().height(), 13.0 );
624 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
625 QCOMPARE( item->rect().width(), 30.0 ); //mm
626 QCOMPARE( item->rect().height(), 130.0 ); //mm
627
628 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
629 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
630 item->dataDefinedProperties().setProperty( QgsLayoutObject::PresetPaperSize, QgsProperty() );
631 item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty::fromValue( "landscape" ) );
632 item->attemptResize( QgsLayoutSize( 1.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
633 QCOMPARE( item->sizeWithUnits().width(), 1.5 );
634 QCOMPARE( item->sizeWithUnits().height(), 1.0 );
635 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
636 QCOMPARE( item->rect().width(), 15.0 ); //mm
637 QCOMPARE( item->rect().height(), 10.0 ); //mm
638 item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty() );
639
640 //check change of units should apply to data defined size
641 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
642 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
643 //first set to same as existing size, but with different units
644 item->attemptResize( QgsLayoutSize( 120.0, 60.0, QgsUnitTypes::LayoutMillimeters ) );
645 //data defined size should utilize new units
646 QCOMPARE( item->sizeWithUnits().width(), 12.0 ); //mm
647 QCOMPARE( item->sizeWithUnits().height(), 6.0 ); //mm
648 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
649 QCOMPARE( item->rect().width(), 12.0 ); //mm
650 QCOMPARE( item->rect().height(), 6.0 ); //mm
651
652 //test that data defined size applies to item's reference point
653 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
654 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
655 item->attemptResize( QgsLayoutSize( 10.0, 5.0, QgsUnitTypes::LayoutMillimeters ) );
656 item->attemptMove( QgsLayoutPoint( 20.0, 10.0, QgsUnitTypes::LayoutMillimeters ) );
657 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "5" ) ) );
658 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "6" ) ) );
659 item->setReferencePoint( QgsLayoutItem::LowerRight );
660 item->refreshItemSize();
661 QCOMPARE( item->scenePos().x(), 25.0 ); //mm
662 QCOMPARE( item->scenePos().y(), 9.0 ); //mm
663
664 //test that data defined size applied after setting item's reference point respects reference
665 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
666 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
667 item->setReferencePoint( QgsLayoutItem::UpperLeft );
668 item->attemptResize( QgsLayoutSize( 10.0, 5.0, QgsUnitTypes::LayoutMillimeters ) );
669 item->attemptMove( QgsLayoutPoint( 20.0, 10.0, QgsUnitTypes::LayoutMillimeters ) );
670 item->setReferencePoint( QgsLayoutItem::LowerRight );
671 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "7" ) ) );
672 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "9" ) ) );
673 item->refreshItemSize();
674 QCOMPARE( item->scenePos().x(), 23.0 ); //mm
675 QCOMPARE( item->scenePos().y(), 6.0 ); //mm
676
677 delete item;
678 }
679
combinedDataDefinedPositionAndSize()680 void TestQgsLayoutItem::combinedDataDefinedPositionAndSize()
681 {
682 QgsProject p;
683 QgsLayout l( &p );
684
685 //test setting data defined size
686 TestItem *item = new TestItem( &l );
687 l.setUnits( QgsUnitTypes::LayoutMillimeters );
688 item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
689 item->attemptResize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
690
691 //test item with all of data defined x, y, width, height set
692 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+7" ) ) );
693 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+3" ) ) );
694 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+9" ) ) );
695 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
696 item->refreshDataDefinedProperty( QgsLayoutObject::AllProperties );
697 QCOMPARE( item->positionWithUnits().x(), 11.0 );
698 QCOMPARE( item->positionWithUnits().y(), 5.0 );
699 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
700 QCOMPARE( item->sizeWithUnits().width(), 13.0 );
701 QCOMPARE( item->sizeWithUnits().height(), 6.0 );
702 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
703 QCOMPARE( item->scenePos().x(), 110.0 ); //mm
704 QCOMPARE( item->scenePos().y(), 50.0 ); //mm
705 QCOMPARE( item->rect().width(), 130.0 ); //mm
706 QCOMPARE( item->rect().height(), 60.0 ); //mm
707
708 //also try with reference point set
709 item->setReferencePoint( QgsLayoutItem::Middle );
710 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
711 item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
712 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "3+7" ) ) );
713 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "1+3" ) ) );
714 item->refreshDataDefinedProperty( QgsLayoutObject::AllProperties );
715 QCOMPARE( item->positionWithUnits().x(), 12.0 );
716 QCOMPARE( item->positionWithUnits().y(), 6.0 );
717 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
718 QCOMPARE( item->sizeWithUnits().width(), 10.0 );
719 QCOMPARE( item->sizeWithUnits().height(), 4.0 );
720 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
721 QCOMPARE( item->scenePos().x(), 70.0 ); //mm
722 QCOMPARE( item->scenePos().y(), 40.0 ); //mm
723 QCOMPARE( item->rect().width(), 100.0 ); //mm
724 QCOMPARE( item->rect().height(), 40.0 ); //mm
725
726 delete item;
727 }
728
729 //TODO rotation
730
resize()731 void TestQgsLayoutItem::resize()
732 {
733 QgsProject p;
734 QgsLayout l( &p );
735
736 //resize test item (no restrictions), same units as layout
737 l.setUnits( QgsUnitTypes::LayoutMillimeters );
738 std::unique_ptr< TestItem > item( new TestItem( &l ) );
739 const QSignalSpy spySizeChanged( item.get(), &QgsLayoutItem::sizePositionChanged );
740
741 item->setRect( 0, 0, 55, 45 );
742 item->attemptMove( QgsLayoutPoint( 27, 29 ) );
743 QCOMPARE( spySizeChanged.count(), 1 );
744 item->attemptResize( QgsLayoutSize( 100.0, 200.0, QgsUnitTypes::LayoutMillimeters ) );
745 QCOMPARE( spySizeChanged.count(), 2 );
746 QCOMPARE( item->rect().width(), 100.0 );
747 QCOMPARE( item->rect().height(), 200.0 );
748 QCOMPARE( item->scenePos().x(), 27.0 ); //item should not move
749 QCOMPARE( item->scenePos().y(), 29.0 );
750
751 //test conversion of units
752 l.setUnits( QgsUnitTypes::LayoutCentimeters );
753 item->setRect( 0, 0, 100, 200 );
754 item->attemptResize( QgsLayoutSize( 0.30, 0.45, QgsUnitTypes::LayoutMeters ) );
755 QCOMPARE( item->rect().width(), 30.0 );
756 QCOMPARE( item->rect().height(), 45.0 );
757 QCOMPARE( spySizeChanged.count(), 4 );
758
759 //test pixel -> page conversion
760 l.setUnits( QgsUnitTypes::LayoutInches );
761 l.renderContext().setDpi( 100.0 );
762 item->refresh();
763 QCOMPARE( spySizeChanged.count(), 6 );
764 item->setRect( 0, 0, 1, 2 );
765 item->attemptResize( QgsLayoutSize( 140, 280, QgsUnitTypes::LayoutPixels ) );
766 QCOMPARE( item->rect().width(), 1.4 );
767 QCOMPARE( item->rect().height(), 2.8 );
768 QCOMPARE( spySizeChanged.count(), 7 );
769 //changing the dpi should resize the item
770 l.renderContext().setDpi( 200.0 );
771 item->refresh();
772 QCOMPARE( item->rect().width(), 0.7 );
773 QCOMPARE( item->rect().height(), 1.4 );
774 QCOMPARE( spySizeChanged.count(), 8 );
775
776 //test page -> pixel conversion
777 l.setUnits( QgsUnitTypes::LayoutPixels );
778 l.renderContext().setDpi( 100.0 );
779 item->refresh();
780 item->setRect( 0, 0, 2, 2 );
781 QCOMPARE( spySizeChanged.count(), 10 );
782 item->attemptResize( QgsLayoutSize( 1, 3, QgsUnitTypes::LayoutInches ) );
783 QCOMPARE( item->rect().width(), 100.0 );
784 QCOMPARE( item->rect().height(), 300.0 );
785 QCOMPARE( spySizeChanged.count(), 11 );
786 //changing dpi results in item resize
787 l.renderContext().setDpi( 200.0 );
788 item->refresh();
789 QCOMPARE( item->rect().width(), 200.0 );
790 QCOMPARE( item->rect().height(), 600.0 );
791 QCOMPARE( spySizeChanged.count(), 13 );
792
793 l.setUnits( QgsUnitTypes::LayoutMillimeters );
794
795 // try override item size in item
796 item->forceResize = true;
797 item->attemptResize( QgsLayoutSize( 10.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
798 QCOMPARE( item->rect().width(), 17.0 );
799 QCOMPARE( item->rect().height(), 27.0 );
800 }
801
referencePoint()802 void TestQgsLayoutItem::referencePoint()
803 {
804 QgsProject p;
805 QgsLayout l( &p );
806
807 //test setting/getting reference point
808 std::unique_ptr< TestItem > item( new TestItem( &l ) );
809 item->setReferencePoint( QgsLayoutItem::LowerMiddle );
810 QCOMPARE( item->referencePoint(), QgsLayoutItem::LowerMiddle );
811
812 //test that setting reference point results in positionWithUnits returning position at new reference
813 //point
814 item->setReferencePoint( QgsLayoutItem::UpperLeft );
815 item->attemptResize( QgsLayoutSize( 2, 4 ) );
816 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
817 QCOMPARE( item->positionWithUnits().x(), 1.0 );
818 QCOMPARE( item->positionWithUnits().y(), 2.0 );
819 item->setReferencePoint( QgsLayoutItem::UpperLeft );
820 QCOMPARE( item->positionWithUnits().x(), 1.0 );
821 QCOMPARE( item->positionWithUnits().y(), 2.0 );
822 item->setReferencePoint( QgsLayoutItem::UpperMiddle );
823 QCOMPARE( item->positionWithUnits().x(), 2.0 );
824 QCOMPARE( item->positionWithUnits().y(), 2.0 );
825 item->setReferencePoint( QgsLayoutItem::UpperRight );
826 QCOMPARE( item->positionWithUnits().x(), 3.0 );
827 QCOMPARE( item->positionWithUnits().y(), 2.0 );
828 item->setReferencePoint( QgsLayoutItem::MiddleLeft );
829 QCOMPARE( item->positionWithUnits().x(), 1.0 );
830 QCOMPARE( item->positionWithUnits().y(), 4.0 );
831 item->setReferencePoint( QgsLayoutItem::Middle );
832 QCOMPARE( item->positionWithUnits().x(), 2.0 );
833 QCOMPARE( item->positionWithUnits().y(), 4.0 );
834 item->setReferencePoint( QgsLayoutItem::MiddleRight );
835 QCOMPARE( item->positionWithUnits().x(), 3.0 );
836 QCOMPARE( item->positionWithUnits().y(), 4.0 );
837 item->setReferencePoint( QgsLayoutItem::LowerLeft );
838 QCOMPARE( item->positionWithUnits().x(), 1.0 );
839 QCOMPARE( item->positionWithUnits().y(), 6.0 );
840 item->setReferencePoint( QgsLayoutItem::LowerMiddle );
841 QCOMPARE( item->positionWithUnits().x(), 2.0 );
842 QCOMPARE( item->positionWithUnits().y(), 6.0 );
843 item->setReferencePoint( QgsLayoutItem::LowerRight );
844 QCOMPARE( item->positionWithUnits().x(), 3.0 );
845 QCOMPARE( item->positionWithUnits().y(), 6.0 );
846
847 item.reset( new TestItem( &l ) );
848
849 //test that setting item position is done relative to reference point
850 l.setUnits( QgsUnitTypes::LayoutMillimeters );
851 item->attemptResize( QgsLayoutSize( 2, 4 ) );
852 item->setReferencePoint( QgsLayoutItem::UpperLeft );
853 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
854 QCOMPARE( item->scenePos().x(), 1.0 );
855 QCOMPARE( item->scenePos().y(), 2.0 );
856 item->setReferencePoint( QgsLayoutItem::UpperMiddle );
857 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
858 QCOMPARE( item->scenePos().x(), 0.0 );
859 QCOMPARE( item->scenePos().y(), 2.0 );
860 item->setReferencePoint( QgsLayoutItem::UpperRight );
861 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
862 QCOMPARE( item->scenePos().x(), -1.0 );
863 QCOMPARE( item->scenePos().y(), 2.0 );
864 item->setReferencePoint( QgsLayoutItem::MiddleLeft );
865 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
866 QCOMPARE( item->scenePos().x(), 1.0 );
867 QCOMPARE( item->scenePos().y(), 0.0 );
868 item->setReferencePoint( QgsLayoutItem::Middle );
869 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
870 QCOMPARE( item->scenePos().x(), 0.0 );
871 QCOMPARE( item->scenePos().y(), 0.0 );
872 item->setReferencePoint( QgsLayoutItem::MiddleRight );
873 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
874 QCOMPARE( item->scenePos().x(), -1.0 );
875 QCOMPARE( item->scenePos().y(), 0.0 );
876 item->setReferencePoint( QgsLayoutItem::LowerLeft );
877 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
878 QCOMPARE( item->scenePos().x(), 1.0 );
879 QCOMPARE( item->scenePos().y(), -2.0 );
880 item->setReferencePoint( QgsLayoutItem::LowerMiddle );
881 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
882 QCOMPARE( item->scenePos().x(), 0.0 );
883 QCOMPARE( item->scenePos().y(), -2.0 );
884 item->setReferencePoint( QgsLayoutItem::LowerRight );
885 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
886 QCOMPARE( item->scenePos().x(), -1.0 );
887 QCOMPARE( item->scenePos().y(), -2.0 );
888
889 item.reset( new TestItem( &l ) );
890
891 //test that resizing is done relative to reference point
892 item->attemptResize( QgsLayoutSize( 2, 4 ) );
893 item->setReferencePoint( QgsLayoutItem::UpperLeft );
894 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
895 item->attemptResize( QgsLayoutSize( 4, 6 ) );
896 QCOMPARE( item->scenePos().x(), 1.0 );
897 QCOMPARE( item->scenePos().y(), 2.0 );
898 item->setReferencePoint( QgsLayoutItem::UpperMiddle );
899 item->attemptResize( QgsLayoutSize( 6, 4 ) );
900 QCOMPARE( item->scenePos().x(), 0.0 );
901 QCOMPARE( item->scenePos().y(), 2.0 );
902 item->setReferencePoint( QgsLayoutItem::UpperRight );
903 item->attemptResize( QgsLayoutSize( 4, 6 ) );
904 QCOMPARE( item->scenePos().x(), 2.0 );
905 QCOMPARE( item->scenePos().y(), 2.0 );
906 item->setReferencePoint( QgsLayoutItem::MiddleLeft );
907 item->attemptResize( QgsLayoutSize( 6, 4 ) );
908 QCOMPARE( item->scenePos().x(), 2.0 );
909 QCOMPARE( item->scenePos().y(), 3.0 );
910 item->setReferencePoint( QgsLayoutItem::Middle );
911 item->attemptResize( QgsLayoutSize( 4, 6 ) );
912 QCOMPARE( item->scenePos().x(), 3.0 );
913 QCOMPARE( item->scenePos().y(), 2.0 );
914 item->setReferencePoint( QgsLayoutItem::MiddleRight );
915 item->attemptResize( QgsLayoutSize( 6, 4 ) );
916 QCOMPARE( item->scenePos().x(), 1.0 );
917 QCOMPARE( item->scenePos().y(), 3.0 );
918 item->setReferencePoint( QgsLayoutItem::LowerLeft );
919 item->attemptResize( QgsLayoutSize( 4, 6 ) );
920 QCOMPARE( item->scenePos().x(), 1.0 );
921 QCOMPARE( item->scenePos().y(), 1.0 );
922 item->setReferencePoint( QgsLayoutItem::LowerMiddle );
923 item->attemptResize( QgsLayoutSize( 6, 4 ) );
924 QCOMPARE( item->scenePos().x(), 0.0 );
925 QCOMPARE( item->scenePos().y(), 3.0 );
926 item->setReferencePoint( QgsLayoutItem::LowerRight );
927 item->attemptResize( QgsLayoutSize( 4, 6 ) );
928 QCOMPARE( item->scenePos().x(), 2.0 );
929 QCOMPARE( item->scenePos().y(), 1.0 );
930
931 item.reset( new TestItem( &l ) );
932
933 //item with frame
934 item->attemptResize( QgsLayoutSize( 2, 4 ) );
935 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
936 item->setFrameEnabled( true );
937 item->setFrameStrokeWidth( QgsLayoutMeasurement( 1 ) );
938 item->attemptResize( QgsLayoutSize( 4, 6 ) );
939 QCOMPARE( item->sizeWithUnits().width(), 4.0 );
940 QCOMPARE( item->sizeWithUnits().height(), 6.0 );
941 item->attemptResize( QgsLayoutSize( 4, 6 ), true );
942 QCOMPARE( item->sizeWithUnits().width(), 3.0 );
943 QCOMPARE( item->sizeWithUnits().height(), 5.0 );
944 }
945
itemPositionReferencePoint()946 void TestQgsLayoutItem::itemPositionReferencePoint()
947 {
948 QgsProject p;
949 QgsLayout l( &p );
950
951 TestItem *item = new TestItem( &l );
952 QPointF result = item->itemPositionAtReferencePoint( QgsLayoutItem::UpperLeft, QSizeF( 2, 4 ) );
953 QCOMPARE( result.x(), 0.0 );
954 QCOMPARE( result.y(), 0.0 );
955 result = item->itemPositionAtReferencePoint( QgsLayoutItem::UpperMiddle, QSizeF( 2, 4 ) );
956 QCOMPARE( result.x(), 1.0 );
957 QCOMPARE( result.y(), 0.0 );
958 result = item->itemPositionAtReferencePoint( QgsLayoutItem::UpperRight, QSizeF( 2, 4 ) );
959 QCOMPARE( result.x(), 2.0 );
960 QCOMPARE( result.y(), 0.0 );
961 result = item->itemPositionAtReferencePoint( QgsLayoutItem::MiddleLeft, QSizeF( 2, 4 ) );
962 QCOMPARE( result.x(), 0.0 );
963 QCOMPARE( result.y(), 2.0 );
964 result = item->itemPositionAtReferencePoint( QgsLayoutItem::Middle, QSizeF( 2, 4 ) );
965 QCOMPARE( result.x(), 1.0 );
966 QCOMPARE( result.y(), 2.0 );
967 result = item->itemPositionAtReferencePoint( QgsLayoutItem::MiddleRight, QSizeF( 2, 4 ) );
968 QCOMPARE( result.x(), 2.0 );
969 QCOMPARE( result.y(), 2.0 );
970 result = item->itemPositionAtReferencePoint( QgsLayoutItem::LowerLeft, QSizeF( 2, 4 ) );
971 QCOMPARE( result.x(), 0.0 );
972 QCOMPARE( result.y(), 4.0 );
973 result = item->itemPositionAtReferencePoint( QgsLayoutItem::LowerMiddle, QSizeF( 2, 4 ) );
974 QCOMPARE( result.x(), 1.0 );
975 QCOMPARE( result.y(), 4.0 );
976 result = item->itemPositionAtReferencePoint( QgsLayoutItem::LowerRight, QSizeF( 2, 4 ) );
977 QCOMPARE( result.x(), 2.0 );
978 QCOMPARE( result.y(), 4.0 );
979
980 delete item;
981 }
982
adjustPointForReference()983 void TestQgsLayoutItem::adjustPointForReference()
984 {
985 QgsProject p;
986 QgsLayout l( &p );
987
988 std::unique_ptr< TestItem > item( new TestItem( &l ) );
989 QPointF result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::UpperLeft );
990 QCOMPARE( result.x(), 5.0 );
991 QCOMPARE( result.y(), 7.0 );
992 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::UpperMiddle );
993 QCOMPARE( result.x(), 4.0 );
994 QCOMPARE( result.y(), 7.0 );
995 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::UpperRight );
996 QCOMPARE( result.x(), 3.0 );
997 QCOMPARE( result.y(), 7.0 );
998 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::MiddleLeft );
999 QCOMPARE( result.x(), 5.0 );
1000 QCOMPARE( result.y(), 5.0 );
1001 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::Middle );
1002 QCOMPARE( result.x(), 4.0 );
1003 QCOMPARE( result.y(), 5.0 );
1004 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::MiddleRight );
1005 QCOMPARE( result.x(), 3.0 );
1006 QCOMPARE( result.y(), 5.0 );
1007 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::LowerLeft );
1008 QCOMPARE( result.x(), 5.0 );
1009 QCOMPARE( result.y(), 3.0 );
1010 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::LowerMiddle );
1011 QCOMPARE( result.x(), 4.0 );
1012 QCOMPARE( result.y(), 3.0 );
1013 result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::LowerRight );
1014 QCOMPARE( result.x(), 3.0 );
1015 QCOMPARE( result.y(), 3.0 );
1016 }
1017
positionAtReferencePoint()1018 void TestQgsLayoutItem::positionAtReferencePoint()
1019 {
1020 QgsProject p;
1021 QgsLayout l( &p );
1022
1023 TestItem *item = new TestItem( &l );
1024 item->setPos( 8.0, 6.0 );
1025 item->setRect( 0.0, 0.0, 4.0, 6.0 );
1026 QPointF result = item->positionAtReferencePoint( QgsLayoutItem::UpperLeft );
1027 QCOMPARE( result.x(), 8.0 );
1028 QCOMPARE( result.y(), 6.0 );
1029 result = item->positionAtReferencePoint( QgsLayoutItem::UpperMiddle );
1030 QCOMPARE( result.x(), 10.0 );
1031 QCOMPARE( result.y(), 6.0 );
1032 result = item->positionAtReferencePoint( QgsLayoutItem::UpperRight );
1033 QCOMPARE( result.x(), 12.0 );
1034 QCOMPARE( result.y(), 6.0 );
1035 result = item->positionAtReferencePoint( QgsLayoutItem::MiddleLeft );
1036 QCOMPARE( result.x(), 8.0 );
1037 QCOMPARE( result.y(), 9.0 );
1038 result = item->positionAtReferencePoint( QgsLayoutItem::Middle );
1039 QCOMPARE( result.x(), 10.0 );
1040 QCOMPARE( result.y(), 9.0 );
1041 result = item->positionAtReferencePoint( QgsLayoutItem::MiddleRight );
1042 QCOMPARE( result.x(), 12.0 );
1043 QCOMPARE( result.y(), 9.0 );
1044 result = item->positionAtReferencePoint( QgsLayoutItem::LowerLeft );
1045 QCOMPARE( result.x(), 8.0 );
1046 QCOMPARE( result.y(), 12.0 );
1047 result = item->positionAtReferencePoint( QgsLayoutItem::LowerMiddle );
1048 QCOMPARE( result.x(), 10.0 );
1049 QCOMPARE( result.y(), 12.0 );
1050 result = item->positionAtReferencePoint( QgsLayoutItem::LowerRight );
1051 QCOMPARE( result.x(), 12.0 );
1052 QCOMPARE( result.y(), 12.0 );
1053
1054 //test with a rotated item
1055 item->setItemRotation( 90 );
1056 result = item->positionAtReferencePoint( QgsLayoutItem::UpperLeft );
1057 QCOMPARE( result.x(), 13.0 );
1058 QCOMPARE( result.y(), 7.0 );
1059 result = item->positionAtReferencePoint( QgsLayoutItem::UpperMiddle );
1060 QCOMPARE( result.x(), 13.0 );
1061 QCOMPARE( result.y(), 9.0 );
1062 result = item->positionAtReferencePoint( QgsLayoutItem::UpperRight );
1063 QCOMPARE( result.x(), 13.0 );
1064 QCOMPARE( result.y(), 11.0 );
1065 result = item->positionAtReferencePoint( QgsLayoutItem::MiddleLeft );
1066 QCOMPARE( result.x(), 10.0 );
1067 QCOMPARE( result.y(), 7.0 );
1068 result = item->positionAtReferencePoint( QgsLayoutItem::Middle );
1069 QCOMPARE( result.x(), 10.0 );
1070 QCOMPARE( result.y(), 9.0 );
1071 result = item->positionAtReferencePoint( QgsLayoutItem::MiddleRight );
1072 QCOMPARE( result.x(), 10.0 );
1073 QCOMPARE( result.y(), 11.0 );
1074 result = item->positionAtReferencePoint( QgsLayoutItem::LowerLeft );
1075 QCOMPARE( result.x(), 7.0 );
1076 QCOMPARE( result.y(), 7.0 );
1077 result = item->positionAtReferencePoint( QgsLayoutItem::LowerMiddle );
1078 QCOMPARE( result.x(), 7.0 );
1079 QCOMPARE( result.y(), 9.0 );
1080 result = item->positionAtReferencePoint( QgsLayoutItem::LowerRight );
1081 QCOMPARE( result.x(), 7.0 );
1082 QCOMPARE( result.y(), 11.0 );
1083
1084 delete item;
1085 }
1086
fixedSize()1087 void TestQgsLayoutItem::fixedSize()
1088 {
1089 QgsProject p;
1090 QgsLayout l( &p );
1091
1092 l.setUnits( QgsUnitTypes::LayoutMillimeters );
1093 std::unique_ptr< FixedSizedItem > item( new FixedSizedItem( &l ) );
1094 QCOMPARE( item->fixedSize().width(), 2.0 );
1095 QCOMPARE( item->fixedSize().height(), 4.0 );
1096 QCOMPARE( item->fixedSize().units(), QgsUnitTypes::LayoutInches );
1097
1098 item->setRect( 0, 0, 5.0, 6.0 ); //temporarily set rect to random size
1099 item->attemptResize( QgsLayoutSize( 7.0, 8.0, QgsUnitTypes::LayoutPoints ) );
1100 //check size matches fixed item size converted to mm
1101 QGSCOMPARENEAR( item->rect().width(), 2.0 * 25.4, 4 * std::numeric_limits<double>::epsilon() );
1102 QGSCOMPARENEAR( item->rect().height(), 4.0 * 25.4, 4 * std::numeric_limits<double>::epsilon() );
1103
1104 item->attemptResize( QgsLayoutSize( 7.0, 8.0, QgsUnitTypes::LayoutInches ) );
1105 //check size matches fixed item size converted to mm
1106 QGSCOMPARENEAR( item->rect().width(), 2.0 * 25.4, 4 * std::numeric_limits<double>::epsilon() );
1107 QGSCOMPARENEAR( item->rect().height(), 4.0 * 25.4, 4 * std::numeric_limits<double>::epsilon() );
1108
1109 //check that setting a fixed size applies this size immediately
1110 item->updateFixedSize( QgsLayoutSize( 150, 250, QgsUnitTypes::LayoutMillimeters ) );
1111 QGSCOMPARENEAR( item->rect().width(), 150.0, 4 * std::numeric_limits<double>::epsilon() );
1112 QGSCOMPARENEAR( item->rect().height(), 250.0, 4 * std::numeric_limits<double>::epsilon() );
1113 }
1114
minSize()1115 void TestQgsLayoutItem::minSize()
1116 {
1117 QgsProject p;
1118 QgsLayout l( &p );
1119
1120 l.setUnits( QgsUnitTypes::LayoutMillimeters );
1121 std::unique_ptr< MinSizedItem > item( new MinSizedItem( &l ) );
1122 QCOMPARE( item->minimumSize().width(), 5.0 );
1123 QCOMPARE( item->minimumSize().height(), 10.0 );
1124 QCOMPARE( item->minimumSize().units(), QgsUnitTypes::LayoutCentimeters );
1125
1126 item->setRect( 0, 0, 9.0, 6.0 ); //temporarily set rect to random size
1127 //try to resize to less than minimum size
1128 item->attemptResize( QgsLayoutSize( 1.0, 0.5, QgsUnitTypes::LayoutPoints ) );
1129 //check size matches min item size converted to mm
1130 QGSCOMPARENEAR( item->rect().width(), 50.0, 4 * std::numeric_limits<double>::epsilon() );
1131 QGSCOMPARENEAR( item->rect().height(), 100.0, 4 * std::numeric_limits<double>::epsilon() );
1132
1133 //check that resize to larger than min size works
1134 item->attemptResize( QgsLayoutSize( 0.1, 0.2, QgsUnitTypes::LayoutMeters ) );
1135 QGSCOMPARENEAR( item->rect().width(), 100.0, 4 * std::numeric_limits<double>::epsilon() );
1136 QGSCOMPARENEAR( item->rect().height(), 200.0, 4 * std::numeric_limits<double>::epsilon() );
1137
1138 //check that setting a minimum size applies this size immediately
1139 item->updateMinSize( QgsLayoutSize( 150, 250, QgsUnitTypes::LayoutMillimeters ) );
1140 QGSCOMPARENEAR( item->rect().width(), 150.0, 4 * std::numeric_limits<double>::epsilon() );
1141 QGSCOMPARENEAR( item->rect().height(), 250.0, 4 * std::numeric_limits<double>::epsilon() );
1142
1143 //also need check that fixed size trumps min size
1144 std::unique_ptr< FixedMinSizedItem > fixedMinItem( new FixedMinSizedItem( &l ) );
1145 QCOMPARE( fixedMinItem->minimumSize().width(), 5.0 );
1146 QCOMPARE( fixedMinItem->minimumSize().height(), 9.0 );
1147 QCOMPARE( fixedMinItem->minimumSize().units(), QgsUnitTypes::LayoutCentimeters );
1148 QCOMPARE( fixedMinItem->fixedSize().width(), 2.0 );
1149 QCOMPARE( fixedMinItem->fixedSize().height(), 4.0 );
1150 QCOMPARE( fixedMinItem->fixedSize().units(), QgsUnitTypes::LayoutCentimeters );
1151 //try to resize to less than minimum size
1152 fixedMinItem->attemptResize( QgsLayoutSize( 1.0, 0.5, QgsUnitTypes::LayoutPoints ) );
1153 //check size matches fixed item size, not minimum size (converted to mm)
1154 QGSCOMPARENEAR( fixedMinItem->rect().width(), 20.0, 4 * std::numeric_limits<double>::epsilon() );
1155 QGSCOMPARENEAR( fixedMinItem->rect().height(), 40.0, 4 * std::numeric_limits<double>::epsilon() );
1156 }
1157
move()1158 void TestQgsLayoutItem::move()
1159 {
1160 QgsProject p;
1161 QgsLayout l( &p );
1162
1163 //move test item, same units as layout
1164 l.setUnits( QgsUnitTypes::LayoutMillimeters );
1165 std::unique_ptr< TestItem > item( new TestItem( &l ) );
1166 item->setRect( 0, 0, 55, 45 );
1167 item->setPos( 27, 29 );
1168 item->attemptMove( QgsLayoutPoint( 60.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
1169 QCOMPARE( item->rect().width(), 55.0 ); //size should not change
1170 QCOMPARE( item->rect().height(), 45.0 );
1171 QCOMPARE( item->scenePos().x(), 60.0 );
1172 QCOMPARE( item->scenePos().y(), 15.0 );
1173
1174 //test conversion of units
1175 l.setUnits( QgsUnitTypes::LayoutCentimeters );
1176 item->setPos( 100, 200 );
1177 item->attemptMove( QgsLayoutPoint( 0.30, 0.45, QgsUnitTypes::LayoutMeters ) );
1178 QCOMPARE( item->scenePos().x(), 30.0 );
1179 QCOMPARE( item->scenePos().y(), 45.0 );
1180
1181 //test pixel -> page conversion
1182 l.setUnits( QgsUnitTypes::LayoutInches );
1183 l.renderContext().setDpi( 100.0 );
1184 item->refresh();
1185 item->setPos( 1, 2 );
1186 item->attemptMove( QgsLayoutPoint( 140, 280, QgsUnitTypes::LayoutPixels ) );
1187 QCOMPARE( item->scenePos().x(), 1.4 );
1188 QCOMPARE( item->scenePos().y(), 2.8 );
1189 //changing the dpi should move the item
1190 l.renderContext().setDpi( 200.0 );
1191 item->refresh();
1192 QCOMPARE( item->scenePos().x(), 0.7 );
1193 QCOMPARE( item->scenePos().y(), 1.4 );
1194
1195 //test page -> pixel conversion
1196 l.setUnits( QgsUnitTypes::LayoutPixels );
1197 l.renderContext().setDpi( 100.0 );
1198 item->refresh();
1199 item->setPos( 2, 2 );
1200 item->attemptMove( QgsLayoutPoint( 1, 3, QgsUnitTypes::LayoutInches ) );
1201 QCOMPARE( item->scenePos().x(), 100.0 );
1202 QCOMPARE( item->scenePos().y(), 300.0 );
1203 //changing dpi results in item move
1204 l.renderContext().setDpi( 200.0 );
1205 item->refresh();
1206 QCOMPARE( item->scenePos().x(), 200.0 );
1207 QCOMPARE( item->scenePos().y(), 600.0 );
1208
1209 l.setUnits( QgsUnitTypes::LayoutMillimeters );
1210
1211 //reference points
1212 item->attemptMove( QgsLayoutPoint( 5, 9 ) );
1213 item->attemptResize( QgsLayoutSize( 4, 6 ) );
1214 item->setReferencePoint( QgsLayoutItem::LowerRight );
1215 QCOMPARE( item->positionWithUnits().x(), 9.0 );
1216 QCOMPARE( item->positionWithUnits().y(), 15.0 );
1217
1218 item->attemptMove( QgsLayoutPoint( 11, 13 ) );
1219 QCOMPARE( item->positionWithUnits().x(), 11.0 );
1220 QCOMPARE( item->positionWithUnits().y(), 13.0 );
1221 QCOMPARE( item->scenePos().x(), 7.0 );
1222 QCOMPARE( item->scenePos().y(), 7.0 );
1223
1224 item->attemptMove( QgsLayoutPoint( 10, 12 ), false );
1225 QCOMPARE( item->positionWithUnits().x(), 14.0 );
1226 QCOMPARE( item->positionWithUnits().y(), 18.0 );
1227 QCOMPARE( item->scenePos().x(), 10.0 );
1228 QCOMPARE( item->scenePos().y(), 12.0 );
1229
1230 //moveBy
1231 item.reset( new TestItem( &l ) );
1232 item->attemptMove( QgsLayoutPoint( 5, 9, QgsUnitTypes::LayoutCentimeters ) );
1233 item->attemptResize( QgsLayoutSize( 4, 6 ) );
1234 QCOMPARE( item->positionWithUnits().x(), 5.0 );
1235 QCOMPARE( item->positionWithUnits().y(), 9.0 );
1236 QCOMPARE( item->scenePos().x(), 50.0 );
1237 QCOMPARE( item->scenePos().y(), 90.0 );
1238 item->attemptMoveBy( 5, -6 );
1239 QCOMPARE( item->positionWithUnits().x(), 5.5 );
1240 QCOMPARE( item->positionWithUnits().y(), 8.4 );
1241 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
1242 QCOMPARE( item->scenePos().x(), 55.0 );
1243 QCOMPARE( item->scenePos().y(), 84.0 );
1244
1245 //item with frame
1246 item.reset( new TestItem( &l ) );
1247 item->attemptResize( QgsLayoutSize( 2, 4 ) );
1248 item->attemptMove( QgsLayoutPoint( 1, 2 ) );
1249 item->setFrameEnabled( true );
1250 item->setFrameStrokeWidth( QgsLayoutMeasurement( 1 ) );
1251 item->attemptMove( QgsLayoutPoint( 1, 3 ) );
1252 QCOMPARE( item->positionWithUnits().x(), 1.0 );
1253 QCOMPARE( item->positionWithUnits().y(), 3.0 );
1254 item->attemptMove( QgsLayoutPoint( 4, 6 ), false, true );
1255 QCOMPARE( item->positionWithUnits().x(), 4.5 );
1256 QCOMPARE( item->positionWithUnits().y(), 6.5 );
1257 }
1258
setSceneRect()1259 void TestQgsLayoutItem::setSceneRect()
1260 {
1261 QgsProject p;
1262 QgsLayout l( &p );
1263
1264 //resize test item (no restrictions), same units as layout
1265 l.setUnits( QgsUnitTypes::LayoutMillimeters );
1266 std::unique_ptr< TestItem > item( new TestItem( &l ) );
1267 const QSignalSpy spySizeChanged( item.get(), &QgsLayoutItem::sizePositionChanged );
1268
1269 item->attemptSetSceneRect( QRectF( 27.0, 29.0, 100, 200 ) );
1270 QCOMPARE( spySizeChanged.count(), 1 );
1271 QCOMPARE( item->rect().width(), 100.0 );
1272 QCOMPARE( item->rect().height(), 200.0 );
1273 QCOMPARE( item->scenePos().x(), 27.0 );
1274 QCOMPARE( item->scenePos().y(), 29.0 );
1275 QCOMPARE( item->positionWithUnits().x(), 27.0 );
1276 QCOMPARE( item->positionWithUnits().y(), 29.0 );
1277 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
1278 QCOMPARE( item->sizeWithUnits().width(), 100.0 );
1279 QCOMPARE( item->sizeWithUnits().height(), 200.0 );
1280 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
1281
1282 //test conversion of units
1283 item->attemptMove( QgsLayoutPoint( 1, 2, QgsUnitTypes::LayoutCentimeters ) );
1284 item->attemptResize( QgsLayoutSize( 3, 4, QgsUnitTypes::LayoutCentimeters ) );
1285 QCOMPARE( spySizeChanged.count(), 3 );
1286 item->attemptSetSceneRect( QRectF( 27.0, 29.0, 100, 200 ) );
1287 QCOMPARE( item->rect().width(), 100.0 );
1288 QCOMPARE( item->rect().height(), 200.0 );
1289 QCOMPARE( item->scenePos().x(), 27.0 );
1290 QCOMPARE( item->scenePos().y(), 29.0 );
1291 QCOMPARE( spySizeChanged.count(), 4 );
1292
1293 QCOMPARE( item->positionWithUnits().x(), 2.70 );
1294 QCOMPARE( item->positionWithUnits().y(), 2.90 );
1295 QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
1296 QCOMPARE( item->sizeWithUnits().width(), 10.0 );
1297 QCOMPARE( item->sizeWithUnits().height(), 20.0 );
1298 QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
1299 }
1300
page()1301 void TestQgsLayoutItem::page()
1302 {
1303 QgsProject proj;
1304 QgsLayout l( &proj );
1305
1306 TestItem *item = new TestItem( nullptr );
1307 item->attemptMove( QgsLayoutPoint( 5, 5 ) );
1308 // no layout
1309 QCOMPARE( item->page(), -1 );
1310 QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
1311 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
1312
1313 delete item;
1314 item = new TestItem( &l );
1315 item->attemptMove( QgsLayoutPoint( 5, 5 ) );
1316 l.addLayoutItem( item );
1317 QCOMPARE( item->page(), -1 );
1318 QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
1319 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
1320
1321 // add pages
1322 std::unique_ptr< QgsLayoutItemPage > page( new QgsLayoutItemPage( &l ) );
1323 page->setPageSize( QgsLayoutSize( 500, 100, QgsUnitTypes::LayoutMillimeters ) );
1324 l.pageCollection()->addPage( page.release() );
1325 QCOMPARE( item->page(), 0 );
1326 QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
1327 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
1328 item->attemptMove( QgsLayoutPoint( 5, 5 ) );
1329 QCOMPARE( item->page(), 0 );
1330 QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
1331 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
1332 item->attemptMove( QgsLayoutPoint( 5, 120 ) );
1333 QCOMPARE( item->page(), 0 );
1334 QCOMPARE( item->pagePos(), QPointF( 5, 120 ) );
1335 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 120 ) );
1336
1337 // second page
1338 page.reset( new QgsLayoutItemPage( &l ) );
1339 page->setPageSize( QgsLayoutSize( 500, 200, QgsUnitTypes::LayoutMillimeters ) );
1340 l.pageCollection()->addPage( page.release() );
1341 QCOMPARE( item->page(), 1 );
1342 item->attemptMove( QgsLayoutPoint( 5, 190 ) );
1343 QCOMPARE( item->pagePos(), QPointF( 5, 80 ) );
1344 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 80 ) );
1345 QCOMPARE( item->page(), 1 );
1346 item->attemptMove( QgsLayoutPoint( 5, 350 ) );
1347 QCOMPARE( item->page(), 1 );
1348 QCOMPARE( item->pagePos(), QPointF( 5, 240 ) );
1349 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 240 ) );
1350
1351 page.reset( new QgsLayoutItemPage( &l ) );
1352 page->setPageSize( QgsLayoutSize( 500, 200, QgsUnitTypes::LayoutMillimeters ) );
1353 l.pageCollection()->addPage( page.release() );
1354 QCOMPARE( item->page(), 2 );
1355 QCOMPARE( item->pagePos(), QPointF( 5, 30 ) );
1356 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 30 ) );
1357
1358 // x position should not matter
1359 item->attemptMove( QgsLayoutPoint( -50, 350 ) );
1360 QCOMPARE( item->page(), 2 );
1361 QCOMPARE( item->pagePos(), QPointF( -50, 30 ) );
1362 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( -50, 30 ) );
1363 item->attemptMove( QgsLayoutPoint( 55555, 350 ) );
1364 QCOMPARE( item->page(), 2 );
1365 QCOMPARE( item->pagePos(), QPointF( 55555, 30 ) );
1366 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 55555, 30 ) );
1367
1368 // with units
1369 item->attemptMove( QgsLayoutPoint( 5, 35, QgsUnitTypes::LayoutCentimeters ) );
1370 QCOMPARE( item->page(), 2 );
1371 QCOMPARE( item->pagePos(), QPointF( 50, 30 ) );
1372 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 3, QgsUnitTypes::LayoutCentimeters ) );
1373
1374 // move with page
1375 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 0 );
1376 QCOMPARE( item->page(), 0 );
1377 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
1378 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, -1 );
1379 QCOMPARE( item->page(), 0 );
1380 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
1381 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 10000 );
1382 QCOMPARE( item->page(), 0 );
1383 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
1384 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 1 );
1385 QCOMPARE( item->page(), 1 );
1386 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
1387 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 116, QgsUnitTypes::LayoutMillimeters ) );
1388 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 2 );
1389 QCOMPARE( item->page(), 2 );
1390 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
1391 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 326, QgsUnitTypes::LayoutMillimeters ) );
1392 item->attemptMove( QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutCentimeters ), true, false, 2 );
1393 QCOMPARE( item->page(), 2 );
1394 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutCentimeters ) );
1395 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 38, QgsUnitTypes::LayoutCentimeters ) );
1396
1397 // non-top-left reference
1398 item->setReferencePoint( QgsLayoutItem::Middle );
1399 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 0 );
1400 QCOMPARE( item->pagePos(), QPointF( 5, 6 ) );
1401 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6 ) );
1402 item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 1 );
1403 QCOMPARE( item->page(), 1 );
1404 QCOMPARE( item->pagePos(), QPointF( 5, 6 ) );
1405 QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
1406 }
1407
itemVariablesFunction()1408 void TestQgsLayoutItem::itemVariablesFunction()
1409 {
1410 const QgsRectangle extent( 2000, 2800, 2500, 2900 );
1411 QgsLayout l( QgsProject::instance() );
1412
1413 QgsExpression e( QStringLiteral( "map_get( item_variables( 'Map_id' ), 'map_scale' )" ) );
1414 // no map
1415 QgsExpressionContext c = l.createExpressionContext();
1416 QVariant r = e.evaluate( &c );
1417 QVERIFY( !r.isValid() );
1418
1419 QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
1420 map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
1421 map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) );
1422 map->setExtent( extent );
1423 l.addLayoutItem( map );
1424 map->setId( QStringLiteral( "Map_id" ) );
1425
1426 c = l.createExpressionContext();
1427 e.prepare( &c );
1428 r = e.evaluate( &c );
1429 QGSCOMPARENEAR( r.toDouble(), 184764103, 100 );
1430
1431 QgsExpression e2( QStringLiteral( "map_get( item_variables( 'Map_id' ), 'map_crs' )" ) );
1432 r = e2.evaluate( &c );
1433 QCOMPARE( r.toString(), QString( "EPSG:4326" ) );
1434
1435 QgsExpression e3( QStringLiteral( "map_get( item_variables( 'Map_id' ), 'map_crs_definition' )" ) );
1436 r = e3.evaluate( &c );
1437 QCOMPARE( r.toString(), QString( "+proj=longlat +datum=WGS84 +no_defs" ) );
1438
1439 QgsExpression e4( QStringLiteral( "map_get( item_variables( 'Map_id' ), 'map_units' )" ) );
1440 r = e4.evaluate( &c );
1441 QCOMPARE( r.toString(), QString( "degrees" ) );
1442
1443 std::unique_ptr< QgsVectorLayer > layer = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=id_a:integer" ), QStringLiteral( "A" ), QStringLiteral( "memory" ) );
1444 std::unique_ptr< QgsVectorLayer > layer2 = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=id_a:integer" ), QStringLiteral( "B" ), QStringLiteral( "memory" ) );
1445 map->setLayers( QList<QgsMapLayer *>() << layer.get() << layer2.get() );
1446 QgsExpression e5( QStringLiteral( "map_get( item_variables( 'Map_id' ), 'map_layer_ids' )" ) );
1447 r = e5.evaluate( &c );
1448 QCOMPARE( r.toStringList().join( ',' ), QStringLiteral( "%1,%2" ).arg( layer->id(), layer2->id() ) );
1449 e5 = QgsExpression( QStringLiteral( "array_foreach(map_get( item_variables( 'Map_id' ), 'map_layers' ), layer_property(@element, 'name'))" ) );
1450 r = e5.evaluate( &c );
1451 QCOMPARE( r.toStringList().join( ',' ), QStringLiteral( "A,B" ) );
1452 }
1453
variables()1454 void TestQgsLayoutItem::variables()
1455 {
1456 QgsLayout l( QgsProject::instance() );
1457
1458 QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
1459 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::layoutItemScope( map ) );
1460 const int before = scope->variableCount();
1461
1462 QgsExpressionContextUtils::setLayoutItemVariable( map, QStringLiteral( "var" ), 5 );
1463 scope.reset( QgsExpressionContextUtils::layoutItemScope( map ) );
1464 QCOMPARE( scope->variableCount(), before + 1 );
1465 QCOMPARE( scope->variable( QStringLiteral( "var" ) ).toInt(), 5 );
1466
1467 QVariantMap vars;
1468 vars.insert( QStringLiteral( "var2" ), 7 );
1469 QgsExpressionContextUtils::setLayoutItemVariables( map, vars );
1470 scope.reset( QgsExpressionContextUtils::layoutItemScope( map ) );
1471 QCOMPARE( scope->variableCount(), before + 1 );
1472 QVERIFY( !scope->hasVariable( QStringLiteral( "var" ) ) );
1473 QCOMPARE( scope->variable( QStringLiteral( "var2" ) ).toInt(), 7 );
1474 }
1475
mapCreditsFunction()1476 void TestQgsLayoutItem::mapCreditsFunction()
1477 {
1478 const QgsRectangle extent( 2000, 2800, 2500, 2900 );
1479 QgsLayout l( QgsProject::instance() );
1480
1481 QgsExpression e( QStringLiteral( "array_to_string( map_credits( 'Map_id' ) )" ) );
1482 // no map
1483 QgsExpressionContext c = l.createExpressionContext();
1484 QVariant r = e.evaluate( &c );
1485 QVERIFY( !r.isValid() );
1486
1487 QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
1488 map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
1489 map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) );
1490 map->setExtent( extent );
1491 l.addLayoutItem( map );
1492 map->setId( QStringLiteral( "Map_id" ) );
1493
1494 c = l.createExpressionContext();
1495 e.prepare( &c );
1496 r = e.evaluate( &c );
1497 // no layers
1498 QCOMPARE( r.toString(), QString() );
1499
1500 // with layers
1501 std::unique_ptr< QgsVectorLayer > layer = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=id_a:integer" ), QStringLiteral( "A" ), QStringLiteral( "memory" ) );
1502 QgsLayerMetadata metadata;
1503 metadata.setRights( QStringList() << QStringLiteral( "CC BY SA" ) );
1504 layer->setMetadata( metadata );
1505 std::unique_ptr< QgsVectorLayer > layer2 = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=id_a:integer" ), QStringLiteral( "B" ), QStringLiteral( "memory" ) );
1506 metadata.setRights( QStringList() << QStringLiteral( "CC NC" ) );
1507 layer2->setMetadata( metadata );
1508 std::unique_ptr< QgsVectorLayer > layer3 = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=id_a:integer" ), QStringLiteral( "C" ), QStringLiteral( "memory" ) );
1509 metadata.setRights( QStringList() << QStringLiteral( "CC BY SA" ) );
1510 layer3->setMetadata( metadata );
1511 const std::unique_ptr< QgsVectorLayer > layer4 = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=id_a:integer" ), QStringLiteral( "C" ), QStringLiteral( "memory" ) );
1512
1513 map->setLayers( QList<QgsMapLayer *>() << layer.get() << layer2.get() << layer3.get() << layer4.get() );
1514 e.prepare( &c );
1515 QCOMPARE( e.evaluate( &c ).toString(), QStringLiteral( "CC BY SA,CC NC" ) );
1516 map->setLayers( QList<QgsMapLayer *>() << layer.get() << layer3.get() << layer4.get() );
1517 e.prepare( &c );
1518 QCOMPARE( e.evaluate( &c ).toString(), QStringLiteral( "CC BY SA" ) );
1519
1520 QgsExpression e2( QStringLiteral( "array_to_string( map_credits( 'Map_id', include_layer_names:=true ) )" ) );
1521 e2.prepare( &c );
1522 QCOMPARE( e2.evaluate( &c ).toString(), QStringLiteral( "A: CC BY SA,C: CC BY SA" ) );
1523 map->setLayers( QList<QgsMapLayer *>() << layer.get() << layer2.get() << layer3.get() << layer4.get() );
1524 QgsExpression e3( QStringLiteral( "array_to_string( map_credits( 'Map_id', include_layer_names:=true, layer_name_separator:='|' ) )" ) );
1525 e3.prepare( &c );
1526 QCOMPARE( e3.evaluate( &c ).toString(), QStringLiteral( "A|CC BY SA,B|CC NC,C|CC BY SA" ) );
1527
1528 // second map
1529 QgsLayoutItemMap *map2 = new QgsLayoutItemMap( &l );
1530 map2->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
1531 map2->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) );
1532 map2->setExtent( extent );
1533 l.addLayoutItem( map2 );
1534 map2->setId( QStringLiteral( "Map_2" ) );
1535 map2->setLayers( QList<QgsMapLayer *>() << layer.get() << layer4.get() );
1536 QgsExpression e4( QStringLiteral( "array_to_string( map_credits( 'Map_2', include_layer_names:=true ) )" ) );
1537 e4.prepare( &c );
1538 QCOMPARE( e4.evaluate( &c ).toString(), QStringLiteral( "A: CC BY SA" ) );
1539 }
1540
rotation()1541 void TestQgsLayoutItem::rotation()
1542 {
1543 QgsProject proj;
1544 QgsLayout l( &proj );
1545
1546 TestItem *item = new TestItem( &l );
1547
1548 const QSignalSpy spyRotationChanged( item, &QgsLayoutItem::rotationChanged );
1549
1550 l.setUnits( QgsUnitTypes::LayoutMillimeters );
1551 item->setPos( 6.0, 10.0 );
1552 item->setRect( 0.0, 0.0, 10.0, 8.0 );
1553 item->setPen( Qt::NoPen );
1554 QRectF bounds = item->sceneBoundingRect();
1555 QCOMPARE( bounds.left(), 6.0 );
1556 QCOMPARE( bounds.right(), 16.0 );
1557 QCOMPARE( bounds.top(), 10.0 );
1558 QCOMPARE( bounds.bottom(), 18.0 );
1559
1560 item->setItemRotation( 90.0 );
1561 QCOMPARE( item->itemRotation(), 90.0 );
1562 QCOMPARE( item->rotation(), 90.0 );
1563 bounds = item->sceneBoundingRect();
1564 QCOMPARE( bounds.left(), 7.0 );
1565 QCOMPARE( bounds.right(), 15.0 );
1566 QCOMPARE( bounds.top(), 9.0 );
1567 QCOMPARE( bounds.bottom(), 19.0 );
1568 QCOMPARE( spyRotationChanged.count(), 1 );
1569 QCOMPARE( spyRotationChanged.at( 0 ).at( 0 ).toDouble(), 90.0 );
1570
1571
1572 //check that negative angles are preserved as negative
1573 item->setItemRotation( -90.0 );
1574 QCOMPARE( item->itemRotation(), -90.0 );
1575 QCOMPARE( item->rotation(), -90.0 );
1576 bounds = item->sceneBoundingRect();
1577 QCOMPARE( bounds.width(), 8.0 );
1578 QCOMPARE( bounds.height(), 10.0 );
1579 QCOMPARE( spyRotationChanged.count(), 2 );
1580 QCOMPARE( spyRotationChanged.at( 1 ).at( 0 ).toDouble(), -90.0 );
1581
1582 //check that rotating changes stored item position for reference point
1583 item->setItemRotation( 0.0 );
1584 QCOMPARE( spyRotationChanged.count(), 3 );
1585 QCOMPARE( spyRotationChanged.at( 2 ).at( 0 ).toDouble(), 0.0 );
1586
1587 item->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
1588 item->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
1589 item->setItemRotation( 90.0 );
1590 QCOMPARE( item->positionWithUnits().x(), 13.0 );
1591 QCOMPARE( item->positionWithUnits().y(), 6.0 );
1592 QCOMPARE( spyRotationChanged.count(), 4 );
1593 QCOMPARE( spyRotationChanged.at( 3 ).at( 0 ).toDouble(), 90.0 );
1594
1595 //setting item position (for reference point) respects rotation
1596 item->attemptMove( QgsLayoutPoint( 10.0, 8.0 ) );
1597 QCOMPARE( item->scenePos().x(), 10.0 );
1598 QCOMPARE( item->scenePos().y(), 8.0 );
1599 const QRectF p = item->sceneBoundingRect();
1600 qDebug() << p.left();
1601 QCOMPARE( item->sceneBoundingRect().left(), 4.0 );
1602 QCOMPARE( item->sceneBoundingRect().right(), 10.0 );
1603 QCOMPARE( item->sceneBoundingRect().top(), 8.0 );
1604 QCOMPARE( item->sceneBoundingRect().bottom(), 18.0 );
1605
1606 // set rotation, using top left
1607 std::unique_ptr< TestItem > item2( new TestItem( &l ) );
1608 item2->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
1609 item2->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
1610 item2->setItemRotation( 90, false );
1611 QCOMPARE( item2->positionWithUnits().x(), 5.0 );
1612 QCOMPARE( item2->positionWithUnits().y(), 8.0 );
1613 QCOMPARE( item2->pos().x(), 5.0 );
1614 QCOMPARE( item2->pos().y(), 8.0 );
1615 item2->setItemRotation( 180, true );
1616 QCOMPARE( item2->positionWithUnits().x(), 7.0 );
1617 QCOMPARE( item2->positionWithUnits().y(), 16.0 );
1618 QCOMPARE( item2->pos().x(), 7.0 );
1619 QCOMPARE( item2->pos().y(), 16.0 );
1620
1621 // test that refresh rotation doesn't move item (#18037)
1622 item2 = std::make_unique< TestItem >( &l );
1623 item2->setReferencePoint( QgsLayoutItem::Middle );
1624 item2->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
1625 item2->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
1626 item2->setItemRotation( 45 );
1627 QCOMPARE( item2->positionWithUnits().x(), 5.0 );
1628 QCOMPARE( item2->positionWithUnits().y(), 8.0 );
1629 QGSCOMPARENEAR( item2->pos().x(), 3.58, 0.01 );
1630 QGSCOMPARENEAR( item2->pos().y(), 2.343146, 0.01 );
1631 QCOMPARE( item2->rotation(), 45.0 );
1632 item2->refresh();
1633 QCOMPARE( item2->positionWithUnits().x(), 5.0 );
1634 QCOMPARE( item2->positionWithUnits().y(), 8.0 );
1635 QGSCOMPARENEAR( item2->pos().x(), 3.58, 0.01 );
1636 QGSCOMPARENEAR( item2->pos().y(), 2.343146, 0.01 );
1637 QCOMPARE( item2->rotation(), 45.0 );
1638
1639
1640 //TODO also changing size?
1641
1642
1643 //data defined rotation
1644 item->setItemRotation( 0.0 );
1645 QCOMPARE( spyRotationChanged.count(), 5 );
1646 QCOMPARE( spyRotationChanged.at( 4 ).at( 0 ).toDouble(), 0.0 );
1647
1648 item->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
1649 item->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
1650 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemRotation, QgsProperty::fromExpression( QStringLiteral( "90" ) ) );
1651 item->refreshDataDefinedProperty( QgsLayoutObject::ItemRotation );
1652 QCOMPARE( item->itemRotation(), 0.0 ); // should be unchanged
1653 QCOMPARE( item->rotation(), 90.0 );
1654 QCOMPARE( spyRotationChanged.count(), 6 );
1655 QCOMPARE( spyRotationChanged.at( 5 ).at( 0 ).toDouble(), 90.0 );
1656
1657 // rotation should have applied around item center
1658 QCOMPARE( item->positionWithUnits().x(), 13.0 );
1659 QCOMPARE( item->positionWithUnits().y(), 6.0 );
1660 QCOMPARE( item->pos().x(), 13.0 );
1661 QCOMPARE( item->pos().y(), 6.0 );
1662
1663 //also check when refreshing all properties
1664 item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemRotation, QgsProperty::fromExpression( QStringLiteral( "180" ) ) );
1665 item->refreshDataDefinedProperty( QgsLayoutObject::AllProperties );
1666 QCOMPARE( item->itemRotation(), 0.0 ); // should be unchanged
1667 QCOMPARE( item->rotation(), 180.0 );
1668 QCOMPARE( spyRotationChanged.count(), 7 );
1669 QCOMPARE( spyRotationChanged.at( 6 ).at( 0 ).toDouble(), 180.0 );
1670 QCOMPARE( item->positionWithUnits().x(), 15.0 );
1671 QCOMPARE( item->positionWithUnits().y(), 14.0 );
1672 QCOMPARE( item->pos().x(), 15.0 );
1673 QCOMPARE( item->pos().y(), 14.0 );
1674
1675 delete item;
1676
1677 //render check
1678 item = new TestItem( &l );
1679 item->setItemRotation( 0.0 );
1680 item->setPos( 100, 150 );
1681 item->setRect( 0, 0, 200, 100 );
1682 l.addItem( item );
1683 item->setItemRotation( 45 );
1684 l.setSceneRect( 0, 0, 400, 400 );
1685 l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true );
1686 QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 );
1687 image.fill( 0 );
1688 QPainter painter( &image );
1689 l.render( &painter );
1690 painter.end();
1691
1692 const bool result = renderCheck( QStringLiteral( "layoutitem_rotation" ), image, 0 );
1693 delete item;
1694 QVERIFY( result );
1695 }
1696
1697 //TODO rotation tests:
1698 //rotate item around layout point
1699
1700
writeXml()1701 void TestQgsLayoutItem::writeXml()
1702 {
1703 QDomImplementation DomImplementation;
1704 const QDomDocumentType documentType =
1705 DomImplementation.createDocumentType(
1706 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1707 QDomDocument doc( documentType );
1708 QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) );
1709
1710 QgsProject proj;
1711 QgsLayout l( &proj );
1712 TestItem *item = new TestItem( &l );
1713 QVERIFY( item->writeXml( rootNode, doc, QgsReadWriteContext() ) );
1714
1715 //make sure type was written
1716 const QDomElement element = rootNode.firstChildElement();
1717
1718 QCOMPARE( element.nodeName(), QString( "LayoutItem" ) );
1719 QCOMPARE( element.attribute( "type", "" ).toInt(), item->type() );
1720
1721 //check that element has an object node
1722 const QDomNodeList objectNodeList = element.elementsByTagName( QStringLiteral( "LayoutObject" ) );
1723 QCOMPARE( objectNodeList.count(), 1 );
1724
1725 delete item;
1726 }
1727
readXml()1728 void TestQgsLayoutItem::readXml()
1729 {
1730 QDomImplementation DomImplementation;
1731 const QDomDocumentType documentType =
1732 DomImplementation.createDocumentType(
1733 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1734 QDomDocument doc( documentType );
1735
1736 QgsProject proj;
1737 QgsLayout l( &proj );
1738 TestItem *item = new TestItem( &l );
1739
1740 //try reading bad elements
1741 const QDomElement badElement = doc.createElement( QStringLiteral( "bad" ) );
1742 const QDomElement noNode;
1743 QVERIFY( !item->readXml( badElement, doc, QgsReadWriteContext() ) );
1744 QVERIFY( !item->readXml( noNode, doc, QgsReadWriteContext() ) );
1745
1746 //try good element
1747 QDomElement goodElement = doc.createElement( QStringLiteral( "LayoutItem" ) );
1748 goodElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "TestItemType" ) );
1749 QVERIFY( item->readXml( goodElement, doc, QgsReadWriteContext() ) );
1750 delete item;
1751 }
1752
writeReadXmlProperties()1753 void TestQgsLayoutItem::writeReadXmlProperties()
1754 {
1755 QgsProject proj;
1756 QgsLayout l( &proj );
1757 TestItem *original = new TestItem( &l );
1758
1759 original->dataDefinedProperties().setProperty( QgsLayoutObject::TestProperty, QgsProperty::fromExpression( QStringLiteral( "10 + 40" ) ) );
1760
1761 original->setReferencePoint( QgsLayoutItem::MiddleRight );
1762 original->attemptResize( QgsLayoutSize( 6, 8, QgsUnitTypes::LayoutCentimeters ) );
1763 original->attemptMove( QgsLayoutPoint( 0.05, 0.09, QgsUnitTypes::LayoutMeters ) );
1764 original->setItemRotation( 45.0 );
1765 original->setId( QStringLiteral( "test" ) );
1766 original->setLocked( true );
1767 original->setZValue( 55 );
1768 original->setVisible( false );
1769 original->setFrameEnabled( true );
1770 original->setFrameStrokeColor( QColor( 100, 150, 200 ) );
1771 original->setFrameStrokeWidth( QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) );
1772 original->setFrameJoinStyle( Qt::MiterJoin );
1773 original->setBackgroundEnabled( false );
1774 original->setBackgroundColor( QColor( 200, 150, 100 ) );
1775 original->setBlendMode( QPainter::CompositionMode_Darken );
1776 original->setExcludeFromExports( true );
1777 original->setItemOpacity( 0.75 );
1778
1779 std::unique_ptr< QgsLayoutItem > copy = createCopyViaXml( &l, original );
1780
1781 QCOMPARE( copy->uuid(), original->uuid() );
1782 QCOMPARE( copy->id(), original->id() );
1783 const QgsProperty dd = copy->dataDefinedProperties().property( QgsLayoutObject::TestProperty );
1784 QVERIFY( dd );
1785 QVERIFY( dd.isActive() );
1786 QCOMPARE( dd.propertyType(), QgsProperty::ExpressionBasedProperty );
1787 QCOMPARE( copy->referencePoint(), original->referencePoint() );
1788 QCOMPARE( copy->sizeWithUnits(), original->sizeWithUnits() );
1789 QGSCOMPARENEAR( copy->positionWithUnits().x(), original->positionWithUnits().x(), 0.001 );
1790 QGSCOMPARENEAR( copy->positionWithUnits().y(), original->positionWithUnits().y(), 0.001 );
1791 QCOMPARE( copy->positionWithUnits().units(), original->positionWithUnits().units() );
1792 QCOMPARE( copy->itemRotation(), original->itemRotation() );
1793 QGSCOMPARENEAR( copy->pos().x(), original->pos().x(), 0.001 );
1794 QGSCOMPARENEAR( copy->pos().y(), original->pos().y(), 0.001 );
1795 QVERIFY( copy->isLocked() );
1796 QCOMPARE( copy->zValue(), 55.0 );
1797 QVERIFY( !copy->isVisible() );
1798 QVERIFY( copy->frameEnabled() );
1799 QCOMPARE( copy->frameStrokeColor(), QColor( 100, 150, 200 ) );
1800 QCOMPARE( copy->frameStrokeWidth(), QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) );
1801 QCOMPARE( copy->frameJoinStyle(), Qt::MiterJoin );
1802 QVERIFY( !copy->hasBackground() );
1803 QCOMPARE( copy->backgroundColor(), QColor( 200, 150, 100 ) );
1804 QCOMPARE( copy->blendMode(), QPainter::CompositionMode_Darken );
1805 QVERIFY( copy->excludeFromExports( ) );
1806 QCOMPARE( copy->itemOpacity(), 0.75 );
1807 delete original;
1808 }
1809
undoRedo()1810 void TestQgsLayoutItem::undoRedo()
1811 {
1812 QgsProject proj;
1813 QgsLayout l( &proj );
1814
1815 QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
1816 const QString uuid = item->uuid();
1817 QPointer< QgsLayoutItemShape > pItem( item ); // for testing deletion
1818 item->setFrameStrokeColor( QColor( 255, 100, 200 ) );
1819 l.addLayoutItem( item );
1820
1821 QVERIFY( pItem );
1822 QVERIFY( l.items().contains( item ) );
1823 QCOMPARE( l.itemByUuid( uuid ), item );
1824
1825 // undo should delete item
1826 l.undoStack()->stack()->undo();
1827 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1828 QVERIFY( !pItem );
1829 QVERIFY( !l.items().contains( item ) );
1830 QVERIFY( !l.itemByUuid( uuid ) );
1831
1832 // redo should restore
1833 l.undoStack()->stack()->redo();
1834 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1835 item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
1836 QVERIFY( item );
1837 QVERIFY( l.items().contains( item ) );
1838 pItem = item;
1839 QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
1840
1841 //... and repeat!
1842
1843 // undo should delete item
1844 l.undoStack()->stack()->undo();
1845 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1846 QVERIFY( !pItem );
1847 QVERIFY( !l.items().contains( item ) );
1848 QVERIFY( !l.itemByUuid( uuid ) );
1849
1850 // redo should restore
1851 l.undoStack()->stack()->redo();
1852 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1853 item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
1854 QVERIFY( item );
1855 QVERIFY( l.items().contains( item ) );
1856 pItem = item;
1857 QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
1858
1859 // delete item
1860 l.removeLayoutItem( item );
1861
1862 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1863 QVERIFY( !pItem );
1864 QVERIFY( !l.items().contains( item ) );
1865 QVERIFY( !l.itemByUuid( uuid ) );
1866
1867 // undo should restore
1868 l.undoStack()->stack()->undo();
1869 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1870 item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
1871 QVERIFY( item );
1872 QVERIFY( l.items().contains( item ) );
1873 pItem = item;
1874 QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
1875
1876 // another undo should delete item
1877 l.undoStack()->stack()->undo();
1878 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1879 QVERIFY( !pItem );
1880 QVERIFY( !l.items().contains( item ) );
1881 QVERIFY( !l.itemByUuid( uuid ) );
1882
1883 // redo should restore
1884 l.undoStack()->stack()->redo();
1885 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1886 item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
1887 QVERIFY( item );
1888 QVERIFY( l.items().contains( item ) );
1889 pItem = item;
1890 QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
1891
1892 // another redo should delete item
1893 l.undoStack()->stack()->redo();
1894 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1895 QVERIFY( !pItem );
1896 QVERIFY( !l.items().contains( item ) );
1897 QVERIFY( !l.itemByUuid( uuid ) );
1898
1899 }
1900
multiItemUndo()1901 void TestQgsLayoutItem::multiItemUndo()
1902 {
1903 QgsProject proj;
1904 QgsLayout l( &proj );
1905
1906 QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
1907 l.addLayoutItem( item );
1908 item->attemptMove( QgsLayoutPoint( 10, 10 ) );
1909 QgsLayoutItemShape *item2 = new QgsLayoutItemShape( &l );
1910 l.addLayoutItem( item2 );
1911 item2->attemptMove( QgsLayoutPoint( 20, 20 ) );
1912
1913 l.undoStack()->beginCommand( item, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
1914 item->attemptMove( QgsLayoutPoint( 1, 1 ) );
1915 l.undoStack()->endCommand();
1916
1917 l.undoStack()->beginCommand( item2, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
1918 item2->attemptMove( QgsLayoutPoint( 21, 21 ) );
1919 l.undoStack()->endCommand();
1920
1921 // undo should only remove item2 move
1922 l.undoStack()->stack()->undo();
1923 QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 20, 20 ) );
1924 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 1, 1 ) );
1925 l.undoStack()->stack()->undo();
1926 QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 20, 20 ) );
1927 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 10, 10 ) );
1928 }
1929
overlappingUndo()1930 void TestQgsLayoutItem::overlappingUndo()
1931 {
1932 QgsProject proj;
1933 QgsLayout l( &proj );
1934
1935 QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
1936 l.addLayoutItem( item );
1937 item->attemptMove( QgsLayoutPoint( 10, 10 ) );
1938 QgsLayoutItemShape *item2 = new QgsLayoutItemShape( &l );
1939 l.addLayoutItem( item2 );
1940 item2->attemptMove( QgsLayoutPoint( 20, 20 ) );
1941
1942 //commands overlap
1943 l.undoStack()->beginCommand( item, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
1944 item->attemptMove( QgsLayoutPoint( 1, 1 ) );
1945 l.undoStack()->beginCommand( item2, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
1946 item2->attemptMove( QgsLayoutPoint( 21, 21 ) );
1947 l.undoStack()->endCommand();
1948 l.undoStack()->endCommand();
1949
1950 // undo should remove item move
1951 l.undoStack()->stack()->undo();
1952 QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 21, 21 ) );
1953 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 10, 10 ) );
1954 l.undoStack()->stack()->undo();
1955 QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 20, 20 ) );
1956 QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 10, 10 ) );
1957
1958 }
1959
blendMode()1960 void TestQgsLayoutItem::blendMode()
1961 {
1962 QgsProject proj;
1963 QgsLayout l( &proj );
1964
1965 QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
1966 l.addLayoutItem( item );
1967
1968 item->setBlendMode( QPainter::CompositionMode_Darken );
1969 QCOMPARE( item->blendMode(), QPainter::CompositionMode_Darken );
1970 QVERIFY( item->mEffect->isEnabled() );
1971
1972 l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, false );
1973 QVERIFY( !item->mEffect->isEnabled() );
1974 l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, true );
1975 QVERIFY( item->mEffect->isEnabled() );
1976
1977 item->dataDefinedProperties().setProperty( QgsLayoutObject::BlendMode, QgsProperty::fromExpression( "'lighten'" ) );
1978 item->refreshDataDefinedProperty();
1979 QCOMPARE( item->blendMode(), QPainter::CompositionMode_Darken ); // should not change
1980 QCOMPARE( item->mEffect->compositionMode(), QPainter::CompositionMode_Lighten );
1981
1982 QgsLayout l2( QgsProject::instance() );
1983 l2.initializeDefaults();
1984 QgsLayoutItemShape *mComposerRect1 = new QgsLayoutItemShape( &l2 );
1985 mComposerRect1->attemptSetSceneRect( QRectF( 20, 20, 150, 100 ) );
1986 mComposerRect1->setShapeType( QgsLayoutItemShape::Rectangle );
1987 QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
1988 QgsFillSymbol *fillSymbol = new QgsFillSymbol();
1989 fillSymbol->changeSymbolLayer( 0, simpleFill );
1990 simpleFill->setColor( QColor( 255, 150, 0 ) );
1991 simpleFill->setStrokeColor( Qt::black );
1992 mComposerRect1->setSymbol( fillSymbol );
1993 delete fillSymbol;
1994
1995 l2.addLayoutItem( mComposerRect1 );
1996 QgsLayoutItemShape *mComposerRect2 = new QgsLayoutItemShape( &l2 );
1997 mComposerRect2->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) );
1998 mComposerRect2->setShapeType( QgsLayoutItemShape::Rectangle );
1999 l2.addLayoutItem( mComposerRect2 );
2000 QgsSimpleFillSymbolLayer *simpleFill2 = new QgsSimpleFillSymbolLayer();
2001 QgsFillSymbol *fillSymbol2 = new QgsFillSymbol();
2002 fillSymbol2->changeSymbolLayer( 0, simpleFill2 );
2003 simpleFill2->setColor( QColor( 0, 100, 150 ) );
2004 simpleFill2->setStrokeColor( Qt::black );
2005 mComposerRect2->setSymbol( fillSymbol2 );
2006 delete fillSymbol2;
2007
2008 mComposerRect2->setBlendMode( QPainter::CompositionMode_Multiply );
2009
2010 QgsLayoutChecker checker( QStringLiteral( "composereffects_blend" ), &l2 );
2011 checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
2012 QVERIFY( checker.testLayout( mReport ) );
2013 }
2014
opacity()2015 void TestQgsLayoutItem::opacity()
2016 {
2017 QgsProject proj;
2018 QgsLayout l( &proj );
2019 l.initializeDefaults();
2020
2021 QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
2022 QgsFillSymbol *fillSymbol = new QgsFillSymbol();
2023 fillSymbol->changeSymbolLayer( 0, simpleFill );
2024 simpleFill->setColor( QColor( 255, 150, 0 ) );
2025 simpleFill->setStrokeColor( Qt::black );
2026
2027 QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
2028 item->setShapeType( QgsLayoutItemShape::Rectangle );
2029 item->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) );
2030 item->setSymbol( fillSymbol->clone() );
2031
2032 l.addLayoutItem( item );
2033
2034 item->setItemOpacity( 0.75 );
2035 QCOMPARE( item->itemOpacity(), 0.75 );
2036
2037 // we handle opacity ourselves, so QGraphicsItem opacity should never be set
2038 QCOMPARE( item->opacity(), 1.0 );
2039
2040 QgsLayoutChecker checker( QStringLiteral( "composereffects_transparency75" ), &l );
2041 checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
2042 QVERIFY( checker.testLayout( mReport ) );
2043
2044 item->dataDefinedProperties().setProperty( QgsLayoutObject::Opacity, QgsProperty::fromExpression( "35" ) );
2045 item->refreshDataDefinedProperty();
2046 QCOMPARE( item->itemOpacity(), 0.75 ); // should not change
2047 QCOMPARE( item->opacity(), 1.0 );
2048
2049 checker = QgsLayoutChecker( QStringLiteral( "composereffects_transparency35" ), &l );
2050 checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
2051 QVERIFY( checker.testLayout( mReport ) );
2052
2053 // with background and frame
2054 l.removeLayoutItem( item );
2055
2056 QgsLayoutItemLabel *labelItem = new QgsLayoutItemLabel( &l );
2057 l.addLayoutItem( labelItem );
2058 labelItem->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) );
2059 labelItem->setBackgroundEnabled( true );
2060 labelItem->setBackgroundColor( QColor( 40, 140, 240 ) );
2061 labelItem->setFrameEnabled( true );
2062 labelItem->setFrameStrokeColor( QColor( 40, 30, 20 ) );
2063 labelItem->setItemOpacity( 0.5 );
2064 checker = QgsLayoutChecker( QStringLiteral( "composereffects_transparency_bgframe" ), &l );
2065 checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
2066 QVERIFY( checker.testLayout( mReport ) );
2067
2068 QgsLayout l2( QgsProject::instance() );
2069 l2.initializeDefaults();
2070 QgsLayoutItemShape *mComposerRect1 = new QgsLayoutItemShape( &l2 );
2071 mComposerRect1->attemptSetSceneRect( QRectF( 20, 20, 150, 100 ) );
2072 mComposerRect1->setShapeType( QgsLayoutItemShape::Rectangle );
2073 mComposerRect1->setSymbol( fillSymbol->clone() );
2074 delete fillSymbol;
2075
2076 l2.addLayoutItem( mComposerRect1 );
2077 QgsLayoutItemShape *mComposerRect2 = new QgsLayoutItemShape( &l2 );
2078 mComposerRect2->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) );
2079 mComposerRect2->setShapeType( QgsLayoutItemShape::Rectangle );
2080 l2.addLayoutItem( mComposerRect2 );
2081 QgsSimpleFillSymbolLayer *simpleFill2 = new QgsSimpleFillSymbolLayer();
2082 QgsFillSymbol *fillSymbol2 = new QgsFillSymbol();
2083 fillSymbol2->changeSymbolLayer( 0, simpleFill2 );
2084 simpleFill2->setColor( QColor( 0, 100, 150 ) );
2085 simpleFill2->setStrokeColor( Qt::black );
2086 mComposerRect2->setSymbol( fillSymbol2 );
2087 delete fillSymbol2;
2088
2089 mComposerRect2->setItemOpacity( 0.5 );
2090
2091 checker = QgsLayoutChecker( QStringLiteral( "composereffects_transparency" ), &l2 );
2092 checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
2093 QVERIFY( checker.testLayout( mReport ) );
2094 }
2095
excludeFromExports()2096 void TestQgsLayoutItem::excludeFromExports()
2097 {
2098 QgsProject proj;
2099 QgsLayout l( &proj );
2100
2101 std::unique_ptr< QgsLayoutItemPage > page( new QgsLayoutItemPage( &l ) );
2102 page->setPageSize( QgsLayoutSize( 297, 210, QgsUnitTypes::LayoutMillimeters ) );
2103 l.pageCollection()->addPage( page.release() );
2104
2105 QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
2106 std::unique_ptr< QgsFillSymbol > fillSymbol( new QgsFillSymbol() );
2107 fillSymbol->changeSymbolLayer( 0, simpleFill );
2108 simpleFill->setColor( Qt::transparent );
2109 simpleFill->setStrokeColor( Qt::transparent );
2110 l.pageCollection()->setPageStyleSymbol( fillSymbol.get() );
2111
2112 QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
2113 l.addLayoutItem( item );
2114
2115 item->setExcludeFromExports( true );
2116 QVERIFY( item->excludeFromExports() );
2117 item->setExcludeFromExports( false );
2118 QVERIFY( !item->excludeFromExports() );
2119
2120 item->dataDefinedProperties().setProperty( QgsLayoutObject::ExcludeFromExports, QgsProperty::fromExpression( "1" ) );
2121 item->refreshDataDefinedProperty();
2122 QVERIFY( !item->excludeFromExports() ); // should not change
2123 QVERIFY( item->mEvaluatedExcludeFromExports );
2124
2125 item->attemptMove( QgsLayoutPoint( 100, 100 ) );
2126 item->attemptResize( QgsLayoutSize( 200, 200 ) );
2127 l.updateBounds();
2128
2129 QgsLayoutChecker checker( QStringLiteral( "layoutitem_excluded" ), &l );
2130 checker.setControlPathPrefix( QStringLiteral( "layouts" ) );
2131 checker.setSize( QSize( 400, 400 ) );
2132 QVERIFY( checker.testLayout( mReport ) );
2133 }
2134
createCopyViaXml(QgsLayout * layout,QgsLayoutItem * original)2135 std::unique_ptr<QgsLayoutItem> TestQgsLayoutItem::createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original )
2136 {
2137 //save original item to xml
2138 QDomImplementation DomImplementation;
2139 const QDomDocumentType documentType =
2140 DomImplementation.createDocumentType(
2141 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
2142 QDomDocument doc( documentType );
2143 QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) );
2144
2145 original->writeXml( rootNode, doc, QgsReadWriteContext() );
2146
2147 //create new item and restore settings from xml
2148 std::unique_ptr< TestItem > copy = std::make_unique< TestItem >( layout );
2149 copy->readXml( rootNode.firstChildElement(), doc, QgsReadWriteContext() );
2150
2151 return std::move( copy );
2152 }
2153
2154 QGSTEST_MAIN( TestQgsLayoutItem )
2155 #include "testqgslayoutitem.moc"
2156