QGIS/tests/src/core/testqgslayoutitem.cpp

2009 lines
81 KiB
C++

/***************************************************************************
testqgslayoutitem.cpp
-----------------------
begin : June 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgslayoutitem.h"
#include "qgslayoutitemregistry.h"
#include "qgslayout.h"
#include "qgsmultirenderchecker.h"
#include "qgstest.h"
#include "qgsproject.h"
#include "qgsreadwritecontext.h"
#include "qgslayoutitemundocommand.h"
#include "qgslayoutitemmap.h"
#include "qgslayoutitemshape.h"
#include "qgslayouteffect.h"
#include "qgsfillsymbollayer.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutundostack.h"
#include <QObject>
#include <QPainter>
#include <QImage>
#include <QtTest/QSignalSpy>
//simple item for testing, since some methods in QgsLayoutItem are pure virtual
class TestItem : public QgsLayoutItem
{
Q_OBJECT
public:
TestItem( QgsLayout *layout ) : QgsLayoutItem( layout )
{
setFrameEnabled( false );
setBackgroundEnabled( false );
}
//implement pure virtual methods
int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; }
bool forceResize = false;
protected:
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem * = nullptr ) override
{
QPainter *painter = context.painter();
painter->save();
painter->setRenderHint( QPainter::Antialiasing, false );
painter->setPen( Qt::NoPen );
painter->setBrush( QColor( 255, 100, 100, 200 ) );
painter->drawRect( rect() );
painter->restore();
}
QSizeF applyItemSizeConstraint( const QSizeF &targetSize ) override
{
if ( !forceResize )
return targetSize;
return QSizeF( 17, 27 );
}
};
//item with minimum size
class MinSizedItem : public TestItem
{
Q_OBJECT
public:
MinSizedItem( QgsLayout *layout ) : TestItem( layout )
{
setMinimumSize( QgsLayoutSize( 5.0, 10.0, QgsUnitTypes::LayoutCentimeters ) );
}
void updateMinSize( QgsLayoutSize size )
{
setMinimumSize( size );
}
};
//item with fixed size
class FixedSizedItem : public TestItem
{
Q_OBJECT
public:
FixedSizedItem( QgsLayout *layout ) : TestItem( layout )
{
setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutInches ) );
}
void updateFixedSize( QgsLayoutSize size )
{
setFixedSize( size );
}
};
//item with both conflicting fixed and minimum size
class FixedMinSizedItem : public TestItem
{
Q_OBJECT
public:
FixedMinSizedItem( QgsLayout *layout ) : TestItem( layout )
{
setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
setMinimumSize( QgsLayoutSize( 5.0, 9.0, QgsUnitTypes::LayoutCentimeters ) );
}
};
class TestQgsLayoutItem: public QObject
{
Q_OBJECT
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void creation(); //test creation of QgsLayoutItem
void uuid();
void id();
void registry();
void shouldDrawDebug();
void shouldDrawAntialiased();
void preparePainter();
void debugRect();
void draw();
void resize();
void referencePoint();
void itemPositionReferencePoint();
void adjustPointForReference();
void positionAtReferencePoint();
void fixedSize();
void minSize();
void move();
void positionWithUnits();
void sizeWithUnits();
void dataDefinedPosition();
void dataDefinedSize();
void combinedDataDefinedPositionAndSize();
void rotation();
void writeXml();
void readXml();
void writeReadXmlProperties();
void undoRedo();
void multiItemUndo();
void overlappingUndo();
void blendMode();
void opacity();
void excludeFromExports();
void setSceneRect();
void page();
void itemVariablesFunction();
void variables();
private:
QString mReport;
bool renderCheck( QString testName, QImage &image, int mismatchCount );
QgsLayoutItem *createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original );
};
void TestQgsLayoutItem::initTestCase()
{
mReport = QStringLiteral( "<h1>Layout Item Tests</h1>\n" );
}
void TestQgsLayoutItem::cleanupTestCase()
{
QString myReportFile = QDir::tempPath() + QDir::separator() + "qgistest.html";
QFile myFile( myReportFile );
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}
}
void TestQgsLayoutItem::init()
{
}
void TestQgsLayoutItem::cleanup()
{
}
void TestQgsLayoutItem::creation()
{
QgsProject p;
QgsLayout *layout = new QgsLayout( &p );
TestItem *item = new TestItem( layout );
QVERIFY( item );
delete item;
delete layout;
}
void TestQgsLayoutItem::uuid()
{
QgsProject p;
QgsLayout l( &p );
//basic test of uuid
TestItem item( &l );
TestItem item2( &l );
QVERIFY( item.uuid() != item2.uuid() );
}
void TestQgsLayoutItem::id()
{
QgsProject p;
QgsLayout l( &p );
TestItem item( &l );
item.setId( QStringLiteral( "test" ) );
QCOMPARE( item.id(), QStringLiteral( "test" ) );
}
void TestQgsLayoutItem::registry()
{
// test QgsLayoutItemRegistry
QgsLayoutItemRegistry registry;
// empty registry
QVERIFY( !registry.itemMetadata( -1 ) );
QVERIFY( registry.itemTypes().isEmpty() );
QVERIFY( !registry.createItem( 1, nullptr ) );
auto create = []( QgsLayout * layout )->QgsLayoutItem*
{
return new TestItem( layout );
};
auto resolve = []( QVariantMap & props, const QgsPathResolver &, bool )
{
props.clear();
};
QSignalSpy spyTypeAdded( &registry, &QgsLayoutItemRegistry::typeAdded );
QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), create, resolve );
QVERIFY( registry.addLayoutItemType( metadata ) );
QCOMPARE( spyTypeAdded.count(), 1 );
QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), 2 );
QCOMPARE( spyTypeAdded.value( 0 ).at( 1 ).toString(), QStringLiteral( "my type" ) );
// duplicate type id
QVERIFY( !registry.addLayoutItemType( metadata ) );
QCOMPARE( spyTypeAdded.count(), 1 );
//retrieve metadata
QVERIFY( !registry.itemMetadata( -1 ) );
QCOMPARE( registry.itemMetadata( 2 )->visibleName(), QStringLiteral( "my type" ) );
QCOMPARE( registry.itemTypes().count(), 1 );
QCOMPARE( registry.itemTypes().value( 2 ), QStringLiteral( "my type" ) );
QgsLayoutItem *item = registry.createItem( 2, nullptr );
QVERIFY( item );
QVERIFY( dynamic_cast< TestItem *>( item ) );
delete item;
QVariantMap props;
props.insert( QStringLiteral( "a" ), 5 );
registry.resolvePaths( 1, props, QgsPathResolver(), true );
QCOMPARE( props.size(), 1 );
registry.resolvePaths( 2, props, QgsPathResolver(), true );
QVERIFY( props.isEmpty() );
//test populate
QgsLayoutItemRegistry reg2;
QVERIFY( reg2.itemTypes().isEmpty() );
QVERIFY( reg2.populate() );
QVERIFY( !reg2.itemTypes().isEmpty() );
QVERIFY( !reg2.populate() );
}
void TestQgsLayoutItem::shouldDrawDebug()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true );
QVERIFY( item->shouldDrawDebugRect() );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, false );
QVERIFY( !item->shouldDrawDebugRect() );
delete item;
}
void TestQgsLayoutItem::shouldDrawAntialiased()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false );
QVERIFY( !item->shouldDrawAntialiased() );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, true );
QVERIFY( item->shouldDrawAntialiased() );
delete item;
}
void TestQgsLayoutItem::preparePainter()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
//test with no painter
item->preparePainter( nullptr );
//test antialiasing correctly set for painter
QImage image( QSize( 100, 100 ), QImage::Format_ARGB32 );
QPainter painter;
painter.begin( &image );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false );
item->preparePainter( &painter );
QVERIFY( !( painter.renderHints() & QPainter::Antialiasing ) );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, true );
item->preparePainter( &painter );
QVERIFY( painter.renderHints() & QPainter::Antialiasing );
delete item;
}
void TestQgsLayoutItem::debugRect()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
l.addItem( item );
item->setPos( 100, 100 );
item->setRect( 0, 0, 200, 200 );
l.setSceneRect( 0, 0, 400, 400 );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true );
QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 );
image.fill( 0 );
QPainter painter( &image );
l.render( &painter );
painter.end();
bool result = renderCheck( QStringLiteral( "layoutitem_debugrect" ), image, 0 );
QVERIFY( result );
}
void TestQgsLayoutItem::draw()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
l.addItem( item );
item->setPos( 100, 100 );
item->setRect( 0, 0, 200, 200 );
l.setSceneRect( 0, 0, 400, 400 );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false ); //disable antialiasing to limit cross platform differences
QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 );
image.fill( 0 );
QPainter painter( &image );
l.render( &painter );
painter.end();
bool result = renderCheck( QStringLiteral( "layoutitem_draw" ), image, 0 );
QVERIFY( result );
}
bool TestQgsLayoutItem::renderCheck( QString testName, QImage &image, int mismatchCount )
{
mReport += "<h2>" + testName + "</h2>\n";
QString myTmpDir = QDir::tempPath() + QDir::separator();
QString myFileName = myTmpDir + testName + ".png";
image.save( myFileName, "PNG" );
QgsRenderChecker myChecker;
myChecker.setControlPathPrefix( QStringLiteral( "layouts" ) );
myChecker.setControlName( "expected_" + testName );
myChecker.setRenderedImage( myFileName );
bool myResultFlag = myChecker.compareImages( testName, mismatchCount );
mReport += myChecker.report();
return myResultFlag;
}
void TestQgsLayoutItem::positionWithUnits()
{
QgsProject p;
QgsLayout l( &p );
std::unique_ptr< TestItem > item( new TestItem( &l ) );
item->attemptMove( QgsLayoutPoint( 60.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( item->positionWithUnits().x(), 60.0 );
QCOMPARE( item->positionWithUnits().y(), 15.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
item->attemptMove( QgsLayoutPoint( 50.0, 100.0, QgsUnitTypes::LayoutPixels ) );
QCOMPARE( item->positionWithUnits().x(), 50.0 );
QCOMPARE( item->positionWithUnits().y(), 100.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutPixels );
}
void TestQgsLayoutItem::sizeWithUnits()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
item->attemptResize( QgsLayoutSize( 60.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 60.0 );
QCOMPARE( item->sizeWithUnits().height(), 15.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
item->attemptResize( QgsLayoutSize( 50.0, 100.0, QgsUnitTypes::LayoutPixels ) );
QCOMPARE( item->sizeWithUnits().width(), 50.0 );
QCOMPARE( item->sizeWithUnits().height(), 100.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutPixels );
delete item;
}
void TestQgsLayoutItem::dataDefinedPosition()
{
QgsProject p;
QgsLayout l( &p );
//test setting data defined position
TestItem *item = new TestItem( &l );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
item->attemptResize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
// position x
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+7" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::PositionX );
QCOMPARE( item->positionWithUnits().x(), 11.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 110.0 ); //mm
//position y
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+3" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::PositionY );
QCOMPARE( item->positionWithUnits().y(), 5.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().y(), 50.0 ); //mm
//refreshPosition should also respect data defined positioning
item->setPos( 0, 0 );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
item->refreshItemPosition();
QCOMPARE( item->positionWithUnits().x(), 12.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 120.0 ); //mm
QCOMPARE( item->scenePos().y(), 60.0 ); //mm
//also check that data defined position overrides when attempting to move
item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->positionWithUnits().x(), 12.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 120.0 ); //mm
QCOMPARE( item->scenePos().y(), 60.0 ); //mm
//restriction only for x position
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty() );
item->attemptMove( QgsLayoutPoint( 6.0, 1.5, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->positionWithUnits().x(), 12.0 );
QCOMPARE( item->positionWithUnits().y(), 1.5 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 120.0 ); //mm
QCOMPARE( item->scenePos().y(), 15.0 ); //mm
//restriction only for y position
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
item->attemptMove( QgsLayoutPoint( 7.0, 1.5, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->positionWithUnits().x(), 7.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 70.0 ); //mm
QCOMPARE( item->scenePos().y(), 60.0 ); //mm
//check change of units should apply to data defined position
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
//first set to same as existing position, but with different units
item->attemptMove( QgsLayoutPoint( 120.0, 60.0, QgsUnitTypes::LayoutMillimeters ) );
//data defined position should utilize new units
QCOMPARE( item->positionWithUnits().x(), 12.0 ); //mm
QCOMPARE( item->positionWithUnits().y(), 6.0 ); //mm
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
QCOMPARE( item->scenePos().x(), 12.0 ); //mm
QCOMPARE( item->scenePos().y(), 6.0 ); //mm
//test that data defined position applies to item's reference point
item->attemptMove( QgsLayoutPoint( 12.0, 6.0, QgsUnitTypes::LayoutCentimeters ) );
item->setReferencePoint( QgsLayoutItem::LowerRight );
QCOMPARE( item->positionWithUnits().x(), 12.0 ); //cm
QCOMPARE( item->positionWithUnits().y(), 6.0 ); //cm
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 100.0 ); //mm
QCOMPARE( item->scenePos().y(), 20.0 ); //mm
//also check setting data defined position AFTER setting reference point
item->setPos( 0, 0 );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "6+10" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+6" ) ) );
item->refreshItemPosition();
QCOMPARE( item->positionWithUnits().x(), 16.0 ); //cm
QCOMPARE( item->positionWithUnits().y(), 8.0 ); //cm
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 140.0 ); //mm
QCOMPARE( item->scenePos().y(), 40.0 ); //mm
delete item;
}
void TestQgsLayoutItem::dataDefinedSize()
{
QgsProject p;
QgsLayout l( &p );
//test setting data defined size
TestItem *item = new TestItem( &l );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
item->attemptResize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
//width
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+7" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::ItemWidth );
QCOMPARE( item->sizeWithUnits().width(), 11.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 110.0 ); //mm
//height
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+3" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::ItemHeight );
QCOMPARE( item->sizeWithUnits().height(), 5.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().height(), 50.0 ); //mm
//refreshSize should also respect data defined size
item->setRect( 0.0, 0.0, 9.0, 8.0 );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
item->refreshItemSize();
QCOMPARE( item->sizeWithUnits().width(), 12.0 );
QCOMPARE( item->sizeWithUnits().height(), 6.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 120.0 ); //mm
QCOMPARE( item->rect().height(), 60.0 ); //mm
//also check that data defined size overrides when attempting to resize
item->attemptResize( QgsLayoutSize( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 12.0 );
QCOMPARE( item->sizeWithUnits().height(), 6.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 120.0 ); //mm
QCOMPARE( item->rect().height(), 60.0 ); //mm
//restriction only for width
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
item->attemptResize( QgsLayoutSize( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 12.0 );
QCOMPARE( item->sizeWithUnits().height(), 1.5 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 120.0 ); //mm
QCOMPARE( item->rect().height(), 15.0 ); //mm
//restriction only for y position
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 7.0 );
QCOMPARE( item->sizeWithUnits().height(), 6.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 70.0 ); //mm
QCOMPARE( item->rect().height(), 60.0 ); //mm
// data defined page size
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PresetPaperSize, QgsProperty::fromValue( QStringLiteral( "A5" ) ) );
item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 14.8 );
QCOMPARE( item->sizeWithUnits().height(), 21.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 148.0 ); //mm
QCOMPARE( item->rect().height(), 210.0 ); //mm
// data defined height/width should override page size
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromValue( "13.0" ) );
item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 13.0 );
QCOMPARE( item->sizeWithUnits().height(), 21.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 130.0 ); //mm
QCOMPARE( item->rect().height(), 210.0 ); //mm
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromValue( "3.0" ) );
item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 13.0 );
QCOMPARE( item->sizeWithUnits().height(), 3.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 130.0 ); //mm
QCOMPARE( item->rect().height(), 30.0 ); //mm
// data defined orientation
item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty::fromValue( "portrait" ) );
item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 3.0 );
QCOMPARE( item->sizeWithUnits().height(), 13.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 30.0 ); //mm
QCOMPARE( item->rect().height(), 130.0 ); //mm
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PresetPaperSize, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty::fromValue( "landscape" ) );
item->attemptResize( QgsLayoutSize( 1.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->sizeWithUnits().width(), 1.5 );
QCOMPARE( item->sizeWithUnits().height(), 1.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->rect().width(), 15.0 ); //mm
QCOMPARE( item->rect().height(), 10.0 ); //mm
item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty() );
//check change of units should apply to data defined size
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
//first set to same as existing size, but with different units
item->attemptResize( QgsLayoutSize( 120.0, 60.0, QgsUnitTypes::LayoutMillimeters ) );
//data defined size should utilize new units
QCOMPARE( item->sizeWithUnits().width(), 12.0 ); //mm
QCOMPARE( item->sizeWithUnits().height(), 6.0 ); //mm
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
QCOMPARE( item->rect().width(), 12.0 ); //mm
QCOMPARE( item->rect().height(), 6.0 ); //mm
//test that data defined size applies to item's reference point
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
item->attemptResize( QgsLayoutSize( 10.0, 5.0, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 20.0, 10.0, QgsUnitTypes::LayoutMillimeters ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "5" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "6" ) ) );
item->setReferencePoint( QgsLayoutItem::LowerRight );
item->refreshItemSize();
QCOMPARE( item->scenePos().x(), 25.0 ); //mm
QCOMPARE( item->scenePos().y(), 9.0 ); //mm
//test that data defined size applied after setting item's reference point respects reference
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() );
item->setReferencePoint( QgsLayoutItem::UpperLeft );
item->attemptResize( QgsLayoutSize( 10.0, 5.0, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 20.0, 10.0, QgsUnitTypes::LayoutMillimeters ) );
item->setReferencePoint( QgsLayoutItem::LowerRight );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "7" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "9" ) ) );
item->refreshItemSize();
QCOMPARE( item->scenePos().x(), 23.0 ); //mm
QCOMPARE( item->scenePos().y(), 6.0 ); //mm
delete item;
}
void TestQgsLayoutItem::combinedDataDefinedPositionAndSize()
{
QgsProject p;
QgsLayout l( &p );
//test setting data defined size
TestItem *item = new TestItem( &l );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
item->attemptMove( QgsLayoutPoint( 6.0, 1.50, QgsUnitTypes::LayoutCentimeters ) );
item->attemptResize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) );
//test item with all of data defined x, y, width, height set
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+7" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+3" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+9" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::AllProperties );
QCOMPARE( item->positionWithUnits().x(), 11.0 );
QCOMPARE( item->positionWithUnits().y(), 5.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->sizeWithUnits().width(), 13.0 );
QCOMPARE( item->sizeWithUnits().height(), 6.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 110.0 ); //mm
QCOMPARE( item->scenePos().y(), 50.0 ); //mm
QCOMPARE( item->rect().width(), 130.0 ); //mm
QCOMPARE( item->rect().height(), 60.0 ); //mm
//also try with reference point set
item->setReferencePoint( QgsLayoutItem::Middle );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionX, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::PositionY, QgsProperty::fromExpression( QStringLiteral( "2+4" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "3+7" ) ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty::fromExpression( QStringLiteral( "1+3" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::AllProperties );
QCOMPARE( item->positionWithUnits().x(), 12.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->sizeWithUnits().width(), 10.0 );
QCOMPARE( item->sizeWithUnits().height(), 4.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 70.0 ); //mm
QCOMPARE( item->scenePos().y(), 40.0 ); //mm
QCOMPARE( item->rect().width(), 100.0 ); //mm
QCOMPARE( item->rect().height(), 40.0 ); //mm
delete item;
}
//TODO rotation
void TestQgsLayoutItem::resize()
{
QgsProject p;
QgsLayout l( &p );
//resize test item (no restrictions), same units as layout
l.setUnits( QgsUnitTypes::LayoutMillimeters );
std::unique_ptr< TestItem > item( new TestItem( &l ) );
QSignalSpy spySizeChanged( item.get(), &QgsLayoutItem::sizePositionChanged );
item->setRect( 0, 0, 55, 45 );
item->attemptMove( QgsLayoutPoint( 27, 29 ) );
QCOMPARE( spySizeChanged.count(), 1 );
item->attemptResize( QgsLayoutSize( 100.0, 200.0, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( spySizeChanged.count(), 2 );
QCOMPARE( item->rect().width(), 100.0 );
QCOMPARE( item->rect().height(), 200.0 );
QCOMPARE( item->scenePos().x(), 27.0 ); //item should not move
QCOMPARE( item->scenePos().y(), 29.0 );
//test conversion of units
l.setUnits( QgsUnitTypes::LayoutCentimeters );
item->setRect( 0, 0, 100, 200 );
item->attemptResize( QgsLayoutSize( 0.30, 0.45, QgsUnitTypes::LayoutMeters ) );
QCOMPARE( item->rect().width(), 30.0 );
QCOMPARE( item->rect().height(), 45.0 );
QCOMPARE( spySizeChanged.count(), 4 );
//test pixel -> page conversion
l.setUnits( QgsUnitTypes::LayoutInches );
l.renderContext().setDpi( 100.0 );
item->refresh();
QCOMPARE( spySizeChanged.count(), 6 );
item->setRect( 0, 0, 1, 2 );
item->attemptResize( QgsLayoutSize( 140, 280, QgsUnitTypes::LayoutPixels ) );
QCOMPARE( item->rect().width(), 1.4 );
QCOMPARE( item->rect().height(), 2.8 );
QCOMPARE( spySizeChanged.count(), 7 );
//changing the dpi should resize the item
l.renderContext().setDpi( 200.0 );
item->refresh();
QCOMPARE( item->rect().width(), 0.7 );
QCOMPARE( item->rect().height(), 1.4 );
QCOMPARE( spySizeChanged.count(), 8 );
//test page -> pixel conversion
l.setUnits( QgsUnitTypes::LayoutPixels );
l.renderContext().setDpi( 100.0 );
item->refresh();
item->setRect( 0, 0, 2, 2 );
QCOMPARE( spySizeChanged.count(), 10 );
item->attemptResize( QgsLayoutSize( 1, 3, QgsUnitTypes::LayoutInches ) );
QCOMPARE( item->rect().width(), 100.0 );
QCOMPARE( item->rect().height(), 300.0 );
QCOMPARE( spySizeChanged.count(), 11 );
//changing dpi results in item resize
l.renderContext().setDpi( 200.0 );
item->refresh();
QCOMPARE( item->rect().width(), 200.0 );
QCOMPARE( item->rect().height(), 600.0 );
QCOMPARE( spySizeChanged.count(), 13 );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
// try override item size in item
item->forceResize = true;
item->attemptResize( QgsLayoutSize( 10.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( item->rect().width(), 17.0 );
QCOMPARE( item->rect().height(), 27.0 );
}
void TestQgsLayoutItem::referencePoint()
{
QgsProject p;
QgsLayout l( &p );
//test setting/getting reference point
std::unique_ptr< TestItem > item( new TestItem( &l ) );
item->setReferencePoint( QgsLayoutItem::LowerMiddle );
QCOMPARE( item->referencePoint(), QgsLayoutItem::LowerMiddle );
//test that setting reference point results in positionWithUnits returning position at new reference
//point
item->setReferencePoint( QgsLayoutItem::UpperLeft );
item->attemptResize( QgsLayoutSize( 2, 4 ) );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->positionWithUnits().x(), 1.0 );
QCOMPARE( item->positionWithUnits().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperLeft );
QCOMPARE( item->positionWithUnits().x(), 1.0 );
QCOMPARE( item->positionWithUnits().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperMiddle );
QCOMPARE( item->positionWithUnits().x(), 2.0 );
QCOMPARE( item->positionWithUnits().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperRight );
QCOMPARE( item->positionWithUnits().x(), 3.0 );
QCOMPARE( item->positionWithUnits().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::MiddleLeft );
QCOMPARE( item->positionWithUnits().x(), 1.0 );
QCOMPARE( item->positionWithUnits().y(), 4.0 );
item->setReferencePoint( QgsLayoutItem::Middle );
QCOMPARE( item->positionWithUnits().x(), 2.0 );
QCOMPARE( item->positionWithUnits().y(), 4.0 );
item->setReferencePoint( QgsLayoutItem::MiddleRight );
QCOMPARE( item->positionWithUnits().x(), 3.0 );
QCOMPARE( item->positionWithUnits().y(), 4.0 );
item->setReferencePoint( QgsLayoutItem::LowerLeft );
QCOMPARE( item->positionWithUnits().x(), 1.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
item->setReferencePoint( QgsLayoutItem::LowerMiddle );
QCOMPARE( item->positionWithUnits().x(), 2.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
item->setReferencePoint( QgsLayoutItem::LowerRight );
QCOMPARE( item->positionWithUnits().x(), 3.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
item.reset( new TestItem( &l ) );
//test that setting item position is done relative to reference point
l.setUnits( QgsUnitTypes::LayoutMillimeters );
item->attemptResize( QgsLayoutSize( 2, 4 ) );
item->setReferencePoint( QgsLayoutItem::UpperLeft );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), 1.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperMiddle );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), 0.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperRight );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), -1.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::MiddleLeft );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), 1.0 );
QCOMPARE( item->scenePos().y(), 0.0 );
item->setReferencePoint( QgsLayoutItem::Middle );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), 0.0 );
QCOMPARE( item->scenePos().y(), 0.0 );
item->setReferencePoint( QgsLayoutItem::MiddleRight );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), -1.0 );
QCOMPARE( item->scenePos().y(), 0.0 );
item->setReferencePoint( QgsLayoutItem::LowerLeft );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), 1.0 );
QCOMPARE( item->scenePos().y(), -2.0 );
item->setReferencePoint( QgsLayoutItem::LowerMiddle );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), 0.0 );
QCOMPARE( item->scenePos().y(), -2.0 );
item->setReferencePoint( QgsLayoutItem::LowerRight );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
QCOMPARE( item->scenePos().x(), -1.0 );
QCOMPARE( item->scenePos().y(), -2.0 );
item.reset( new TestItem( &l ) );
//test that resizing is done relative to reference point
item->attemptResize( QgsLayoutSize( 2, 4 ) );
item->setReferencePoint( QgsLayoutItem::UpperLeft );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->scenePos().x(), 1.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperMiddle );
item->attemptResize( QgsLayoutSize( 6, 4 ) );
QCOMPARE( item->scenePos().x(), 0.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::UpperRight );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->scenePos().x(), 2.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::MiddleLeft );
item->attemptResize( QgsLayoutSize( 6, 4 ) );
QCOMPARE( item->scenePos().x(), 2.0 );
QCOMPARE( item->scenePos().y(), 3.0 );
item->setReferencePoint( QgsLayoutItem::Middle );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->scenePos().x(), 3.0 );
QCOMPARE( item->scenePos().y(), 2.0 );
item->setReferencePoint( QgsLayoutItem::MiddleRight );
item->attemptResize( QgsLayoutSize( 6, 4 ) );
QCOMPARE( item->scenePos().x(), 1.0 );
QCOMPARE( item->scenePos().y(), 3.0 );
item->setReferencePoint( QgsLayoutItem::LowerLeft );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->scenePos().x(), 1.0 );
QCOMPARE( item->scenePos().y(), 1.0 );
item->setReferencePoint( QgsLayoutItem::LowerMiddle );
item->attemptResize( QgsLayoutSize( 6, 4 ) );
QCOMPARE( item->scenePos().x(), 0.0 );
QCOMPARE( item->scenePos().y(), 3.0 );
item->setReferencePoint( QgsLayoutItem::LowerRight );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->scenePos().x(), 2.0 );
QCOMPARE( item->scenePos().y(), 1.0 );
item.reset( new TestItem( &l ) );
//item with frame
item->attemptResize( QgsLayoutSize( 2, 4 ) );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
item->setFrameEnabled( true );
item->setFrameStrokeWidth( QgsLayoutMeasurement( 1 ) );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->sizeWithUnits().width(), 4.0 );
QCOMPARE( item->sizeWithUnits().height(), 6.0 );
item->attemptResize( QgsLayoutSize( 4, 6 ), true );
QCOMPARE( item->sizeWithUnits().width(), 3.0 );
QCOMPARE( item->sizeWithUnits().height(), 5.0 );
}
void TestQgsLayoutItem::itemPositionReferencePoint()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
QPointF result = item->itemPositionAtReferencePoint( QgsLayoutItem::UpperLeft, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 0.0 );
QCOMPARE( result.y(), 0.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::UpperMiddle, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 1.0 );
QCOMPARE( result.y(), 0.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::UpperRight, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 2.0 );
QCOMPARE( result.y(), 0.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::MiddleLeft, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 0.0 );
QCOMPARE( result.y(), 2.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::Middle, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 1.0 );
QCOMPARE( result.y(), 2.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::MiddleRight, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 2.0 );
QCOMPARE( result.y(), 2.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::LowerLeft, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 0.0 );
QCOMPARE( result.y(), 4.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::LowerMiddle, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 1.0 );
QCOMPARE( result.y(), 4.0 );
result = item->itemPositionAtReferencePoint( QgsLayoutItem::LowerRight, QSizeF( 2, 4 ) );
QCOMPARE( result.x(), 2.0 );
QCOMPARE( result.y(), 4.0 );
delete item;
}
void TestQgsLayoutItem::adjustPointForReference()
{
QgsProject p;
QgsLayout l( &p );
std::unique_ptr< TestItem > item( new TestItem( &l ) );
QPointF result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::UpperLeft );
QCOMPARE( result.x(), 5.0 );
QCOMPARE( result.y(), 7.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::UpperMiddle );
QCOMPARE( result.x(), 4.0 );
QCOMPARE( result.y(), 7.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::UpperRight );
QCOMPARE( result.x(), 3.0 );
QCOMPARE( result.y(), 7.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::MiddleLeft );
QCOMPARE( result.x(), 5.0 );
QCOMPARE( result.y(), 5.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::Middle );
QCOMPARE( result.x(), 4.0 );
QCOMPARE( result.y(), 5.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::MiddleRight );
QCOMPARE( result.x(), 3.0 );
QCOMPARE( result.y(), 5.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::LowerLeft );
QCOMPARE( result.x(), 5.0 );
QCOMPARE( result.y(), 3.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::LowerMiddle );
QCOMPARE( result.x(), 4.0 );
QCOMPARE( result.y(), 3.0 );
result = item->adjustPointForReferencePosition( QPointF( 5, 7 ), QSizeF( 2, 4 ), QgsLayoutItem::LowerRight );
QCOMPARE( result.x(), 3.0 );
QCOMPARE( result.y(), 3.0 );
}
void TestQgsLayoutItem::positionAtReferencePoint()
{
QgsProject p;
QgsLayout l( &p );
TestItem *item = new TestItem( &l );
item->setPos( 8.0, 6.0 );
item->setRect( 0.0, 0.0, 4.0, 6.0 );
QPointF result = item->positionAtReferencePoint( QgsLayoutItem::UpperLeft );
QCOMPARE( result.x(), 8.0 );
QCOMPARE( result.y(), 6.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::UpperMiddle );
QCOMPARE( result.x(), 10.0 );
QCOMPARE( result.y(), 6.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::UpperRight );
QCOMPARE( result.x(), 12.0 );
QCOMPARE( result.y(), 6.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::MiddleLeft );
QCOMPARE( result.x(), 8.0 );
QCOMPARE( result.y(), 9.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::Middle );
QCOMPARE( result.x(), 10.0 );
QCOMPARE( result.y(), 9.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::MiddleRight );
QCOMPARE( result.x(), 12.0 );
QCOMPARE( result.y(), 9.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::LowerLeft );
QCOMPARE( result.x(), 8.0 );
QCOMPARE( result.y(), 12.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::LowerMiddle );
QCOMPARE( result.x(), 10.0 );
QCOMPARE( result.y(), 12.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::LowerRight );
QCOMPARE( result.x(), 12.0 );
QCOMPARE( result.y(), 12.0 );
//test with a rotated item
item->setItemRotation( 90 );
result = item->positionAtReferencePoint( QgsLayoutItem::UpperLeft );
QCOMPARE( result.x(), 13.0 );
QCOMPARE( result.y(), 7.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::UpperMiddle );
QCOMPARE( result.x(), 13.0 );
QCOMPARE( result.y(), 9.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::UpperRight );
QCOMPARE( result.x(), 13.0 );
QCOMPARE( result.y(), 11.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::MiddleLeft );
QCOMPARE( result.x(), 10.0 );
QCOMPARE( result.y(), 7.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::Middle );
QCOMPARE( result.x(), 10.0 );
QCOMPARE( result.y(), 9.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::MiddleRight );
QCOMPARE( result.x(), 10.0 );
QCOMPARE( result.y(), 11.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::LowerLeft );
QCOMPARE( result.x(), 7.0 );
QCOMPARE( result.y(), 7.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::LowerMiddle );
QCOMPARE( result.x(), 7.0 );
QCOMPARE( result.y(), 9.0 );
result = item->positionAtReferencePoint( QgsLayoutItem::LowerRight );
QCOMPARE( result.x(), 7.0 );
QCOMPARE( result.y(), 11.0 );
delete item;
}
void TestQgsLayoutItem::fixedSize()
{
QgsProject p;
QgsLayout l( &p );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
std::unique_ptr< FixedSizedItem > item( new FixedSizedItem( &l ) );
QCOMPARE( item->fixedSize().width(), 2.0 );
QCOMPARE( item->fixedSize().height(), 4.0 );
QCOMPARE( item->fixedSize().units(), QgsUnitTypes::LayoutInches );
item->setRect( 0, 0, 5.0, 6.0 ); //temporarily set rect to random size
item->attemptResize( QgsLayoutSize( 7.0, 8.0, QgsUnitTypes::LayoutPoints ) );
//check size matches fixed item size converted to mm
QGSCOMPARENEAR( item->rect().width(), 2.0 * 25.4, 4 * DBL_EPSILON );
QGSCOMPARENEAR( item->rect().height(), 4.0 * 25.4, 4 * DBL_EPSILON );
item->attemptResize( QgsLayoutSize( 7.0, 8.0, QgsUnitTypes::LayoutInches ) );
//check size matches fixed item size converted to mm
QGSCOMPARENEAR( item->rect().width(), 2.0 * 25.4, 4 * DBL_EPSILON );
QGSCOMPARENEAR( item->rect().height(), 4.0 * 25.4, 4 * DBL_EPSILON );
//check that setting a fixed size applies this size immediately
item->updateFixedSize( QgsLayoutSize( 150, 250, QgsUnitTypes::LayoutMillimeters ) );
QGSCOMPARENEAR( item->rect().width(), 150.0, 4 * DBL_EPSILON );
QGSCOMPARENEAR( item->rect().height(), 250.0, 4 * DBL_EPSILON );
}
void TestQgsLayoutItem::minSize()
{
QgsProject p;
QgsLayout l( &p );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
std::unique_ptr< MinSizedItem > item( new MinSizedItem( &l ) );
QCOMPARE( item->minimumSize().width(), 5.0 );
QCOMPARE( item->minimumSize().height(), 10.0 );
QCOMPARE( item->minimumSize().units(), QgsUnitTypes::LayoutCentimeters );
item->setRect( 0, 0, 9.0, 6.0 ); //temporarily set rect to random size
//try to resize to less than minimum size
item->attemptResize( QgsLayoutSize( 1.0, 0.5, QgsUnitTypes::LayoutPoints ) );
//check size matches min item size converted to mm
QGSCOMPARENEAR( item->rect().width(), 50.0, 4 * DBL_EPSILON );
QGSCOMPARENEAR( item->rect().height(), 100.0, 4 * DBL_EPSILON );
//check that resize to larger than min size works
item->attemptResize( QgsLayoutSize( 0.1, 0.2, QgsUnitTypes::LayoutMeters ) );
QGSCOMPARENEAR( item->rect().width(), 100.0, 4 * DBL_EPSILON );
QGSCOMPARENEAR( item->rect().height(), 200.0, 4 * DBL_EPSILON );
//check that setting a minimum size applies this size immediately
item->updateMinSize( QgsLayoutSize( 150, 250, QgsUnitTypes::LayoutMillimeters ) );
QGSCOMPARENEAR( item->rect().width(), 150.0, 4 * DBL_EPSILON );
QGSCOMPARENEAR( item->rect().height(), 250.0, 4 * DBL_EPSILON );
//also need check that fixed size trumps min size
std::unique_ptr< FixedMinSizedItem > fixedMinItem( new FixedMinSizedItem( &l ) );
QCOMPARE( fixedMinItem->minimumSize().width(), 5.0 );
QCOMPARE( fixedMinItem->minimumSize().height(), 9.0 );
QCOMPARE( fixedMinItem->minimumSize().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( fixedMinItem->fixedSize().width(), 2.0 );
QCOMPARE( fixedMinItem->fixedSize().height(), 4.0 );
QCOMPARE( fixedMinItem->fixedSize().units(), QgsUnitTypes::LayoutCentimeters );
//try to resize to less than minimum size
fixedMinItem->attemptResize( QgsLayoutSize( 1.0, 0.5, QgsUnitTypes::LayoutPoints ) );
//check size matches fixed item size, not minimum size (converted to mm)
QGSCOMPARENEAR( fixedMinItem->rect().width(), 20.0, 4 * DBL_EPSILON );
QGSCOMPARENEAR( fixedMinItem->rect().height(), 40.0, 4 * DBL_EPSILON );
}
void TestQgsLayoutItem::move()
{
QgsProject p;
QgsLayout l( &p );
//move test item, same units as layout
l.setUnits( QgsUnitTypes::LayoutMillimeters );
std::unique_ptr< TestItem > item( new TestItem( &l ) );
item->setRect( 0, 0, 55, 45 );
item->setPos( 27, 29 );
item->attemptMove( QgsLayoutPoint( 60.0, 15.0, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( item->rect().width(), 55.0 ); //size should not change
QCOMPARE( item->rect().height(), 45.0 );
QCOMPARE( item->scenePos().x(), 60.0 );
QCOMPARE( item->scenePos().y(), 15.0 );
//test conversion of units
l.setUnits( QgsUnitTypes::LayoutCentimeters );
item->setPos( 100, 200 );
item->attemptMove( QgsLayoutPoint( 0.30, 0.45, QgsUnitTypes::LayoutMeters ) );
QCOMPARE( item->scenePos().x(), 30.0 );
QCOMPARE( item->scenePos().y(), 45.0 );
//test pixel -> page conversion
l.setUnits( QgsUnitTypes::LayoutInches );
l.renderContext().setDpi( 100.0 );
item->refresh();
item->setPos( 1, 2 );
item->attemptMove( QgsLayoutPoint( 140, 280, QgsUnitTypes::LayoutPixels ) );
QCOMPARE( item->scenePos().x(), 1.4 );
QCOMPARE( item->scenePos().y(), 2.8 );
//changing the dpi should move the item
l.renderContext().setDpi( 200.0 );
item->refresh();
QCOMPARE( item->scenePos().x(), 0.7 );
QCOMPARE( item->scenePos().y(), 1.4 );
//test page -> pixel conversion
l.setUnits( QgsUnitTypes::LayoutPixels );
l.renderContext().setDpi( 100.0 );
item->refresh();
item->setPos( 2, 2 );
item->attemptMove( QgsLayoutPoint( 1, 3, QgsUnitTypes::LayoutInches ) );
QCOMPARE( item->scenePos().x(), 100.0 );
QCOMPARE( item->scenePos().y(), 300.0 );
//changing dpi results in item move
l.renderContext().setDpi( 200.0 );
item->refresh();
QCOMPARE( item->scenePos().x(), 200.0 );
QCOMPARE( item->scenePos().y(), 600.0 );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
//reference points
item->attemptMove( QgsLayoutPoint( 5, 9 ) );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
item->setReferencePoint( QgsLayoutItem::LowerRight );
QCOMPARE( item->positionWithUnits().x(), 9.0 );
QCOMPARE( item->positionWithUnits().y(), 15.0 );
item->attemptMove( QgsLayoutPoint( 11, 13 ) );
QCOMPARE( item->positionWithUnits().x(), 11.0 );
QCOMPARE( item->positionWithUnits().y(), 13.0 );
QCOMPARE( item->scenePos().x(), 7.0 );
QCOMPARE( item->scenePos().y(), 7.0 );
item->attemptMove( QgsLayoutPoint( 10, 12 ), false );
QCOMPARE( item->positionWithUnits().x(), 14.0 );
QCOMPARE( item->positionWithUnits().y(), 18.0 );
QCOMPARE( item->scenePos().x(), 10.0 );
QCOMPARE( item->scenePos().y(), 12.0 );
//moveBy
item.reset( new TestItem( &l ) );
item->attemptMove( QgsLayoutPoint( 5, 9, QgsUnitTypes::LayoutCentimeters ) );
item->attemptResize( QgsLayoutSize( 4, 6 ) );
QCOMPARE( item->positionWithUnits().x(), 5.0 );
QCOMPARE( item->positionWithUnits().y(), 9.0 );
QCOMPARE( item->scenePos().x(), 50.0 );
QCOMPARE( item->scenePos().y(), 90.0 );
item->attemptMoveBy( 5, -6 );
QCOMPARE( item->positionWithUnits().x(), 5.5 );
QCOMPARE( item->positionWithUnits().y(), 8.4 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->scenePos().x(), 55.0 );
QCOMPARE( item->scenePos().y(), 84.0 );
//item with frame
item.reset( new TestItem( &l ) );
item->attemptResize( QgsLayoutSize( 2, 4 ) );
item->attemptMove( QgsLayoutPoint( 1, 2 ) );
item->setFrameEnabled( true );
item->setFrameStrokeWidth( QgsLayoutMeasurement( 1 ) );
item->attemptMove( QgsLayoutPoint( 1, 3 ) );
QCOMPARE( item->positionWithUnits().x(), 1.0 );
QCOMPARE( item->positionWithUnits().y(), 3.0 );
item->attemptMove( QgsLayoutPoint( 4, 6 ), false, true );
QCOMPARE( item->positionWithUnits().x(), 4.5 );
QCOMPARE( item->positionWithUnits().y(), 6.5 );
}
void TestQgsLayoutItem::setSceneRect()
{
QgsProject p;
QgsLayout l( &p );
//resize test item (no restrictions), same units as layout
l.setUnits( QgsUnitTypes::LayoutMillimeters );
std::unique_ptr< TestItem > item( new TestItem( &l ) );
QSignalSpy spySizeChanged( item.get(), &QgsLayoutItem::sizePositionChanged );
item->attemptSetSceneRect( QRectF( 27.0, 29.0, 100, 200 ) );
QCOMPARE( spySizeChanged.count(), 1 );
QCOMPARE( item->rect().width(), 100.0 );
QCOMPARE( item->rect().height(), 200.0 );
QCOMPARE( item->scenePos().x(), 27.0 );
QCOMPARE( item->scenePos().y(), 29.0 );
QCOMPARE( item->positionWithUnits().x(), 27.0 );
QCOMPARE( item->positionWithUnits().y(), 29.0 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
QCOMPARE( item->sizeWithUnits().width(), 100.0 );
QCOMPARE( item->sizeWithUnits().height(), 200.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutMillimeters );
//test conversion of units
item->attemptMove( QgsLayoutPoint( 1, 2, QgsUnitTypes::LayoutCentimeters ) );
item->attemptResize( QgsLayoutSize( 3, 4, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( spySizeChanged.count(), 3 );
item->attemptSetSceneRect( QRectF( 27.0, 29.0, 100, 200 ) );
QCOMPARE( item->rect().width(), 100.0 );
QCOMPARE( item->rect().height(), 200.0 );
QCOMPARE( item->scenePos().x(), 27.0 );
QCOMPARE( item->scenePos().y(), 29.0 );
QCOMPARE( spySizeChanged.count(), 4 );
QCOMPARE( item->positionWithUnits().x(), 2.70 );
QCOMPARE( item->positionWithUnits().y(), 2.90 );
QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
QCOMPARE( item->sizeWithUnits().width(), 10.0 );
QCOMPARE( item->sizeWithUnits().height(), 20.0 );
QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters );
}
void TestQgsLayoutItem::page()
{
QgsProject proj;
QgsLayout l( &proj );
TestItem *item = new TestItem( nullptr );
item->attemptMove( QgsLayoutPoint( 5, 5 ) );
// no layout
QCOMPARE( item->page(), -1 );
QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
delete item;
item = new TestItem( &l );
item->attemptMove( QgsLayoutPoint( 5, 5 ) );
l.addLayoutItem( item );
QCOMPARE( item->page(), -1 );
QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
// add pages
std::unique_ptr< QgsLayoutItemPage > page( new QgsLayoutItemPage( &l ) );
page->setPageSize( QgsLayoutSize( 500, 100, QgsUnitTypes::LayoutMillimeters ) );
l.pageCollection()->addPage( page.release() );
QCOMPARE( item->page(), 0 );
QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
item->attemptMove( QgsLayoutPoint( 5, 5 ) );
QCOMPARE( item->page(), 0 );
QCOMPARE( item->pagePos(), QPointF( 5, 5 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 5 ) );
item->attemptMove( QgsLayoutPoint( 5, 120 ) );
QCOMPARE( item->page(), 0 );
QCOMPARE( item->pagePos(), QPointF( 5, 120 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 120 ) );
// second page
page.reset( new QgsLayoutItemPage( &l ) );
page->setPageSize( QgsLayoutSize( 500, 200, QgsUnitTypes::LayoutMillimeters ) );
l.pageCollection()->addPage( page.release() );
QCOMPARE( item->page(), 1 );
item->attemptMove( QgsLayoutPoint( 5, 190 ) );
QCOMPARE( item->pagePos(), QPointF( 5, 80 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 80 ) );
QCOMPARE( item->page(), 1 );
item->attemptMove( QgsLayoutPoint( 5, 350 ) );
QCOMPARE( item->page(), 1 );
QCOMPARE( item->pagePos(), QPointF( 5, 240 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 240 ) );
page.reset( new QgsLayoutItemPage( &l ) );
page->setPageSize( QgsLayoutSize( 500, 200, QgsUnitTypes::LayoutMillimeters ) );
l.pageCollection()->addPage( page.release() );
QCOMPARE( item->page(), 2 );
QCOMPARE( item->pagePos(), QPointF( 5, 30 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 30 ) );
// x position should not matter
item->attemptMove( QgsLayoutPoint( -50, 350 ) );
QCOMPARE( item->page(), 2 );
QCOMPARE( item->pagePos(), QPointF( -50, 30 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( -50, 30 ) );
item->attemptMove( QgsLayoutPoint( 55555, 350 ) );
QCOMPARE( item->page(), 2 );
QCOMPARE( item->pagePos(), QPointF( 55555, 30 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 55555, 30 ) );
// with units
item->attemptMove( QgsLayoutPoint( 5, 35, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->page(), 2 );
QCOMPARE( item->pagePos(), QPointF( 50, 30 ) );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 3, QgsUnitTypes::LayoutCentimeters ) );
// move with page
item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 0 );
QCOMPARE( item->page(), 0 );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, -1 );
QCOMPARE( item->page(), 0 );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 10000 );
QCOMPARE( item->page(), 0 );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 1 );
QCOMPARE( item->page(), 1 );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 116, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 5, 6 ), true, false, 2 );
QCOMPARE( item->page(), 2 );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutMillimeters ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 326, QgsUnitTypes::LayoutMillimeters ) );
item->attemptMove( QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutCentimeters ), true, false, 2 );
QCOMPARE( item->page(), 2 );
QCOMPARE( item->pagePositionWithUnits(), QgsLayoutPoint( 5, 6, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 38, QgsUnitTypes::LayoutCentimeters ) );
}
void TestQgsLayoutItem::itemVariablesFunction()
{
QgsRectangle extent( 2000, 2800, 2500, 2900 );
QgsLayout l( QgsProject::instance() );
QgsExpression e( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_scale' )" ) );
// no map
QgsExpressionContext c = l.createExpressionContext();
QVariant r = e.evaluate( &c );
QVERIFY( !r.isValid() );
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) );
map->setExtent( extent );
l.addLayoutItem( map );
map->setId( QStringLiteral( "map_id" ) );
c = l.createExpressionContext();
r = e.evaluate( &c );
QGSCOMPARENEAR( r.toDouble(), 184764103, 100 );
QgsExpression e2( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_crs' )" ) );
r = e2.evaluate( &c );
QCOMPARE( r.toString(), QString( "EPSG:4326" ) );
QgsExpression e3( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_crs_definition' )" ) );
r = e3.evaluate( &c );
QCOMPARE( r.toString(), QString( "+proj=longlat +datum=WGS84 +no_defs" ) );
QgsExpression e4( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_units' )" ) );
r = e4.evaluate( &c );
QCOMPARE( r.toString(), QString( "degrees" ) );
}
void TestQgsLayoutItem::variables()
{
QgsLayout l( QgsProject::instance() );
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::layoutItemScope( map ) );
int before = scope->variableCount();
QgsExpressionContextUtils::setLayoutItemVariable( map, QStringLiteral( "var" ), 5 );
scope.reset( QgsExpressionContextUtils::layoutItemScope( map ) );
QCOMPARE( scope->variableCount(), before + 1 );
QCOMPARE( scope->variable( QStringLiteral( "var" ) ).toInt(), 5 );
QVariantMap vars;
vars.insert( QStringLiteral( "var2" ), 7 );
QgsExpressionContextUtils::setLayoutItemVariables( map, vars );
scope.reset( QgsExpressionContextUtils::layoutItemScope( map ) );
QCOMPARE( scope->variableCount(), before + 1 );
QVERIFY( !scope->hasVariable( QStringLiteral( "var" ) ) );
QCOMPARE( scope->variable( QStringLiteral( "var2" ) ).toInt(), 7 );
}
void TestQgsLayoutItem::rotation()
{
QgsProject proj;
QgsLayout l( &proj );
TestItem *item = new TestItem( &l );
QSignalSpy spyRotationChanged( item, &QgsLayoutItem::rotationChanged );
l.setUnits( QgsUnitTypes::LayoutMillimeters );
item->setPos( 6.0, 10.0 );
item->setRect( 0.0, 0.0, 10.0, 8.0 );
item->setPen( Qt::NoPen );
QRectF bounds = item->sceneBoundingRect();
QCOMPARE( bounds.left(), 6.0 );
QCOMPARE( bounds.right(), 16.0 );
QCOMPARE( bounds.top(), 10.0 );
QCOMPARE( bounds.bottom(), 18.0 );
item->setItemRotation( 90.0 );
QCOMPARE( item->itemRotation(), 90.0 );
QCOMPARE( item->rotation(), 90.0 );
bounds = item->sceneBoundingRect();
QCOMPARE( bounds.left(), 7.0 );
QCOMPARE( bounds.right(), 15.0 );
QCOMPARE( bounds.top(), 9.0 );
QCOMPARE( bounds.bottom(), 19.0 );
QCOMPARE( spyRotationChanged.count(), 1 );
QCOMPARE( spyRotationChanged.at( 0 ).at( 0 ).toDouble(), 90.0 );
//check that negative angles are preserved as negative
item->setItemRotation( -90.0 );
QCOMPARE( item->itemRotation(), -90.0 );
QCOMPARE( item->rotation(), -90.0 );
bounds = item->sceneBoundingRect();
QCOMPARE( bounds.width(), 8.0 );
QCOMPARE( bounds.height(), 10.0 );
QCOMPARE( spyRotationChanged.count(), 2 );
QCOMPARE( spyRotationChanged.at( 1 ).at( 0 ).toDouble(), -90.0 );
//check that rotating changes stored item position for reference point
item->setItemRotation( 0.0 );
QCOMPARE( spyRotationChanged.count(), 3 );
QCOMPARE( spyRotationChanged.at( 2 ).at( 0 ).toDouble(), 0.0 );
item->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
item->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
item->setItemRotation( 90.0 );
QCOMPARE( item->positionWithUnits().x(), 13.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
QCOMPARE( spyRotationChanged.count(), 4 );
QCOMPARE( spyRotationChanged.at( 3 ).at( 0 ).toDouble(), 90.0 );
//setting item position (for reference point) respects rotation
item->attemptMove( QgsLayoutPoint( 10.0, 8.0 ) );
QCOMPARE( item->scenePos().x(), 10.0 );
QCOMPARE( item->scenePos().y(), 8.0 );
QRectF p = item->sceneBoundingRect();
qDebug() << p.left();
QCOMPARE( item->sceneBoundingRect().left(), 4.0 );
QCOMPARE( item->sceneBoundingRect().right(), 10.0 );
QCOMPARE( item->sceneBoundingRect().top(), 8.0 );
QCOMPARE( item->sceneBoundingRect().bottom(), 18.0 );
// set rotation, using top left
std::unique_ptr< TestItem > item2( new TestItem( &l ) );
item2->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
item2->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
item2->setItemRotation( 90, false );
QCOMPARE( item2->positionWithUnits().x(), 5.0 );
QCOMPARE( item2->positionWithUnits().y(), 8.0 );
QCOMPARE( item2->pos().x(), 5.0 );
QCOMPARE( item2->pos().y(), 8.0 );
item2->setItemRotation( 180, true );
QCOMPARE( item2->positionWithUnits().x(), 7.0 );
QCOMPARE( item2->positionWithUnits().y(), 16.0 );
QCOMPARE( item2->pos().x(), 7.0 );
QCOMPARE( item2->pos().y(), 16.0 );
//TODO also changing size?
//data defined rotation
item->setItemRotation( 0.0 );
QCOMPARE( spyRotationChanged.count(), 5 );
QCOMPARE( spyRotationChanged.at( 4 ).at( 0 ).toDouble(), 0.0 );
item->attemptMove( QgsLayoutPoint( 5.0, 8.0 ) );
item->attemptResize( QgsLayoutSize( 10.0, 6.0 ) );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemRotation, QgsProperty::fromExpression( QStringLiteral( "90" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::ItemRotation );
QCOMPARE( item->itemRotation(), 0.0 ); // should be unchanged
QCOMPARE( item->rotation(), 90.0 );
QCOMPARE( spyRotationChanged.count(), 6 );
QCOMPARE( spyRotationChanged.at( 5 ).at( 0 ).toDouble(), 90.0 );
// rotation should have applied around item center
QCOMPARE( item->positionWithUnits().x(), 13.0 );
QCOMPARE( item->positionWithUnits().y(), 6.0 );
QCOMPARE( item->pos().x(), 13.0 );
QCOMPARE( item->pos().y(), 6.0 );
//also check when refreshing all properties
item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemRotation, QgsProperty::fromExpression( QStringLiteral( "180" ) ) );
item->refreshDataDefinedProperty( QgsLayoutObject::AllProperties );
QCOMPARE( item->itemRotation(), 0.0 ); // should be unchanged
QCOMPARE( item->rotation(), 180.0 );
QCOMPARE( spyRotationChanged.count(), 7 );
QCOMPARE( spyRotationChanged.at( 6 ).at( 0 ).toDouble(), 180.0 );
QCOMPARE( item->positionWithUnits().x(), 15.0 );
QCOMPARE( item->positionWithUnits().y(), 14.0 );
QCOMPARE( item->pos().x(), 15.0 );
QCOMPARE( item->pos().y(), 14.0 );
delete item;
//render check
item = new TestItem( &l );
item->setItemRotation( 0.0 );
item->setPos( 100, 150 );
item->setRect( 0, 0, 200, 100 );
l.addItem( item );
item->setItemRotation( 45 );
l.setSceneRect( 0, 0, 400, 400 );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true );
QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 );
image.fill( 0 );
QPainter painter( &image );
l.render( &painter );
painter.end();
bool result = renderCheck( QStringLiteral( "layoutitem_rotation" ), image, 0 );
delete item;
QVERIFY( result );
}
//TODO rotation tests:
//rotate item around layout point
void TestQgsLayoutItem::writeXml()
{
QDomImplementation DomImplementation;
QDomDocumentType documentType =
DomImplementation.createDocumentType(
QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
QDomDocument doc( documentType );
QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) );
QgsProject proj;
QgsLayout l( &proj );
TestItem *item = new TestItem( &l );
QVERIFY( item->writeXml( rootNode, doc, QgsReadWriteContext() ) );
//make sure type was written
QDomElement element = rootNode.firstChildElement();
QCOMPARE( element.nodeName(), QString( "LayoutItem" ) );
QCOMPARE( element.attribute( "type", "" ).toInt(), item->type() );
//check that element has an object node
QDomNodeList objectNodeList = element.elementsByTagName( QStringLiteral( "LayoutObject" ) );
QCOMPARE( objectNodeList.count(), 1 );
delete item;
}
void TestQgsLayoutItem::readXml()
{
QDomImplementation DomImplementation;
QDomDocumentType documentType =
DomImplementation.createDocumentType(
QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
QDomDocument doc( documentType );
QgsProject proj;
QgsLayout l( &proj );
TestItem *item = new TestItem( &l );
//try reading bad elements
QDomElement badElement = doc.createElement( QStringLiteral( "bad" ) );
QDomElement noNode;
QVERIFY( !item->readXml( badElement, doc, QgsReadWriteContext() ) );
QVERIFY( !item->readXml( noNode, doc, QgsReadWriteContext() ) );
//try good element
QDomElement goodElement = doc.createElement( QStringLiteral( "LayoutItem" ) );
goodElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "TestItemType" ) );
QVERIFY( item->readXml( goodElement, doc, QgsReadWriteContext() ) );
delete item;
}
void TestQgsLayoutItem::writeReadXmlProperties()
{
QgsProject proj;
QgsLayout l( &proj );
TestItem *original = new TestItem( &l );
original->dataDefinedProperties().setProperty( QgsLayoutObject::TestProperty, QgsProperty::fromExpression( QStringLiteral( "10 + 40" ) ) );
original->setReferencePoint( QgsLayoutItem::Middle );
original->attemptResize( QgsLayoutSize( 6, 8, QgsUnitTypes::LayoutCentimeters ) );
original->attemptMove( QgsLayoutPoint( 0.05, 0.09, QgsUnitTypes::LayoutMeters ) );
original->setItemRotation( 45.0 );
original->setId( QStringLiteral( "test" ) );
original->setLocked( true );
original->setZValue( 55 );
original->setVisible( false );
original->setFrameEnabled( true );
original->setFrameStrokeColor( QColor( 100, 150, 200 ) );
original->setFrameStrokeWidth( QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) );
original->setFrameJoinStyle( Qt::MiterJoin );
original->setBackgroundEnabled( false );
original->setBackgroundColor( QColor( 200, 150, 100 ) );
original->setBlendMode( QPainter::CompositionMode_Darken );
original->setExcludeFromExports( true );
original->setItemOpacity( 0.75 );
QgsLayoutItem *copy = createCopyViaXml( &l, original );
QCOMPARE( copy->uuid(), original->uuid() );
QCOMPARE( copy->id(), original->id() );
QgsProperty dd = copy->dataDefinedProperties().property( QgsLayoutObject::TestProperty );
QVERIFY( dd );
QVERIFY( dd.isActive() );
QCOMPARE( dd.propertyType(), QgsProperty::ExpressionBasedProperty );
QCOMPARE( copy->referencePoint(), original->referencePoint() );
QCOMPARE( copy->sizeWithUnits(), original->sizeWithUnits() );
QCOMPARE( copy->positionWithUnits(), original->positionWithUnits() );
QCOMPARE( copy->itemRotation(), original->itemRotation() );
QVERIFY( copy->isLocked() );
QCOMPARE( copy->zValue(), 55.0 );
QVERIFY( !copy->isVisible() );
QVERIFY( copy->frameEnabled() );
QCOMPARE( copy->frameStrokeColor(), QColor( 100, 150, 200 ) );
QCOMPARE( copy->frameStrokeWidth(), QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( copy->frameJoinStyle(), Qt::MiterJoin );
QVERIFY( !copy->hasBackground() );
QCOMPARE( copy->backgroundColor(), QColor( 200, 150, 100 ) );
QCOMPARE( copy->blendMode(), QPainter::CompositionMode_Darken );
QVERIFY( copy->excludeFromExports( ) );
QCOMPARE( copy->itemOpacity(), 0.75 );
delete copy;
delete original;
}
void TestQgsLayoutItem::undoRedo()
{
QgsProject proj;
QgsLayout l( &proj );
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
QString uuid = item->uuid();
QPointer< QgsLayoutItemShape > pItem( item ); // for testing deletion
item->setFrameStrokeColor( QColor( 255, 100, 200 ) );
l.addLayoutItem( item );
QVERIFY( pItem );
QVERIFY( l.items().contains( item ) );
QCOMPARE( l.itemByUuid( uuid ), item );
// undo should delete item
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );
// redo should restore
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
//... and repeat!
// undo should delete item
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );
// redo should restore
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
// delete item
l.removeLayoutItem( item );
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );
// undo should restore
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
// another undo should delete item
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );
// redo should restore
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );
// another redo should delete item
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );
}
void TestQgsLayoutItem::multiItemUndo()
{
QgsProject proj;
QgsLayout l( &proj );
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
l.addLayoutItem( item );
item->attemptMove( QgsLayoutPoint( 10, 10 ) );
QgsLayoutItemShape *item2 = new QgsLayoutItemShape( &l );
l.addLayoutItem( item2 );
item2->attemptMove( QgsLayoutPoint( 20, 20 ) );
l.undoStack()->beginCommand( item, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
item->attemptMove( QgsLayoutPoint( 1, 1 ) );
l.undoStack()->endCommand();
l.undoStack()->beginCommand( item2, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
item2->attemptMove( QgsLayoutPoint( 21, 21 ) );
l.undoStack()->endCommand();
// undo should only remove item2 move
l.undoStack()->stack()->undo();
QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 20, 20 ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 1, 1 ) );
l.undoStack()->stack()->undo();
QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 20, 20 ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 10, 10 ) );
}
void TestQgsLayoutItem::overlappingUndo()
{
QgsProject proj;
QgsLayout l( &proj );
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
l.addLayoutItem( item );
item->attemptMove( QgsLayoutPoint( 10, 10 ) );
QgsLayoutItemShape *item2 = new QgsLayoutItemShape( &l );
l.addLayoutItem( item2 );
item2->attemptMove( QgsLayoutPoint( 20, 20 ) );
//commands overlap
l.undoStack()->beginCommand( item, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
item->attemptMove( QgsLayoutPoint( 1, 1 ) );
l.undoStack()->beginCommand( item2, tr( "Item moved" ), QgsLayoutItem::UndoIncrementalMove );
item2->attemptMove( QgsLayoutPoint( 21, 21 ) );
l.undoStack()->endCommand();
l.undoStack()->endCommand();
// undo should remove item move
l.undoStack()->stack()->undo();
QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 21, 21 ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 10, 10 ) );
l.undoStack()->stack()->undo();
QCOMPARE( item2->positionWithUnits(), QgsLayoutPoint( 20, 20 ) );
QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 10, 10 ) );
}
void TestQgsLayoutItem::blendMode()
{
QgsProject proj;
QgsLayout l( &proj );
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
l.addLayoutItem( item );
item->setBlendMode( QPainter::CompositionMode_Darken );
QCOMPARE( item->blendMode(), QPainter::CompositionMode_Darken );
QVERIFY( item->mEffect->isEnabled() );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, false );
QVERIFY( !item->mEffect->isEnabled() );
l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, true );
QVERIFY( item->mEffect->isEnabled() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::BlendMode, QgsProperty::fromExpression( "'lighten'" ) );
item->refreshDataDefinedProperty();
QCOMPARE( item->blendMode(), QPainter::CompositionMode_Darken ); // should not change
QCOMPARE( item->mEffect->compositionMode(), QPainter::CompositionMode_Lighten );
QgsLayout l2( QgsProject::instance() );
l2.initializeDefaults();
QgsLayoutItemShape *mComposerRect1 = new QgsLayoutItemShape( &l2 );
mComposerRect1->attemptSetSceneRect( QRectF( 20, 20, 150, 100 ) );
mComposerRect1->setShapeType( QgsLayoutItemShape::Rectangle );
QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
QgsFillSymbol *fillSymbol = new QgsFillSymbol();
fillSymbol->changeSymbolLayer( 0, simpleFill );
simpleFill->setColor( QColor( 255, 150, 0 ) );
mComposerRect1->setSymbol( fillSymbol );
delete fillSymbol;
l2.addLayoutItem( mComposerRect1 );
QgsLayoutItemShape *mComposerRect2 = new QgsLayoutItemShape( &l2 );
mComposerRect2->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) );
mComposerRect2->setShapeType( QgsLayoutItemShape::Rectangle );
l2.addLayoutItem( mComposerRect2 );
QgsSimpleFillSymbolLayer *simpleFill2 = new QgsSimpleFillSymbolLayer();
QgsFillSymbol *fillSymbol2 = new QgsFillSymbol();
fillSymbol2->changeSymbolLayer( 0, simpleFill2 );
simpleFill2->setColor( QColor( 0, 100, 150 ) );
mComposerRect2->setSymbol( fillSymbol2 );
delete fillSymbol2;
mComposerRect2->setBlendMode( QPainter::CompositionMode_Multiply );
QgsLayoutChecker checker( QStringLiteral( "composereffects_blend" ), &l2 );
checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
QVERIFY( checker.testLayout( mReport ) );
}
void TestQgsLayoutItem::opacity()
{
QgsProject proj;
QgsLayout l( &proj );
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
l.addLayoutItem( item );
item->setItemOpacity( 0.75 );
QCOMPARE( item->itemOpacity(), 0.75 );
QCOMPARE( item->opacity(), 0.75 );
item->dataDefinedProperties().setProperty( QgsLayoutObject::Opacity, QgsProperty::fromExpression( "35" ) );
item->refreshDataDefinedProperty();
QCOMPARE( item->itemOpacity(), 0.75 ); // should not change
QCOMPARE( item->opacity(), 0.35 );
QgsLayout l2( QgsProject::instance() );
l2.initializeDefaults();
QgsLayoutItemShape *mComposerRect1 = new QgsLayoutItemShape( &l2 );
mComposerRect1->attemptSetSceneRect( QRectF( 20, 20, 150, 100 ) );
mComposerRect1->setShapeType( QgsLayoutItemShape::Rectangle );
QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
QgsFillSymbol *fillSymbol = new QgsFillSymbol();
fillSymbol->changeSymbolLayer( 0, simpleFill );
simpleFill->setColor( QColor( 255, 150, 0 ) );
mComposerRect1->setSymbol( fillSymbol );
delete fillSymbol;
l2.addLayoutItem( mComposerRect1 );
QgsLayoutItemShape *mComposerRect2 = new QgsLayoutItemShape( &l2 );
mComposerRect2->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) );
mComposerRect2->setShapeType( QgsLayoutItemShape::Rectangle );
l2.addLayoutItem( mComposerRect2 );
QgsSimpleFillSymbolLayer *simpleFill2 = new QgsSimpleFillSymbolLayer();
QgsFillSymbol *fillSymbol2 = new QgsFillSymbol();
fillSymbol2->changeSymbolLayer( 0, simpleFill2 );
simpleFill2->setColor( QColor( 0, 100, 150 ) );
mComposerRect2->setSymbol( fillSymbol2 );
delete fillSymbol2;
mComposerRect2->setItemOpacity( 0.5 );
QgsLayoutChecker checker( QStringLiteral( "composereffects_transparency" ), &l2 );
checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) );
QVERIFY( checker.testLayout( mReport ) );
}
void TestQgsLayoutItem::excludeFromExports()
{
QgsProject proj;
QgsLayout l( &proj );
std::unique_ptr< QgsLayoutItemPage > page( new QgsLayoutItemPage( &l ) );
page->setPageSize( QgsLayoutSize( 297, 210, QgsUnitTypes::LayoutMillimeters ) );
l.pageCollection()->addPage( page.release() );
QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
std::unique_ptr< QgsFillSymbol > fillSymbol( new QgsFillSymbol() );
fillSymbol->changeSymbolLayer( 0, simpleFill );
simpleFill->setColor( Qt::transparent );
simpleFill->setStrokeColor( Qt::transparent );
l.pageCollection()->setPageStyleSymbol( fillSymbol.get() );
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
l.addLayoutItem( item );
item->setExcludeFromExports( true );
QVERIFY( item->excludeFromExports() );
item->setExcludeFromExports( false );
QVERIFY( !item->excludeFromExports() );
item->dataDefinedProperties().setProperty( QgsLayoutObject::ExcludeFromExports, QgsProperty::fromExpression( "1" ) );
item->refreshDataDefinedProperty();
QVERIFY( !item->excludeFromExports() ); // should not change
QVERIFY( item->mEvaluatedExcludeFromExports );
item->attemptMove( QgsLayoutPoint( 100, 100 ) );
item->attemptResize( QgsLayoutSize( 200, 200 ) );
l.updateBounds();
QgsLayoutChecker checker( QStringLiteral( "layoutitem_excluded" ), &l );
checker.setControlPathPrefix( QStringLiteral( "layouts" ) );
checker.setSize( QSize( 400, 400 ) );
QVERIFY( checker.testLayout( mReport ) );
}
QgsLayoutItem *TestQgsLayoutItem::createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original )
{
//save original item to xml
QDomImplementation DomImplementation;
QDomDocumentType documentType =
DomImplementation.createDocumentType(
QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
QDomDocument doc( documentType );
QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) );
original->writeXml( rootNode, doc, QgsReadWriteContext() );
//create new item and restore settings from xml
TestItem *copy = new TestItem( layout );
copy->readXml( rootNode.firstChildElement(), doc, QgsReadWriteContext() );
return copy;
}
QGSTEST_MAIN( TestQgsLayoutItem )
#include "testqgslayoutitem.moc"