1 /***************************************************************************
2 testqgslayoutview.cpp
3 --------------------
4 Date : July 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgstest.h"
17 #include "qgslayout.h"
18 #include "qgslayoutview.h"
19 #include "qgslayoutviewtool.h"
20 #include "qgslayoutviewmouseevent.h"
21 #include "qgslayoutitem.h"
22 #include "qgslayoutviewrubberband.h"
23 #include "qgslayoutitemregistry.h"
24 #include "qgslayoutitemguiregistry.h"
25 #include "qgslayoutitemwidget.h"
26 #include "qgslayoutitemshape.h"
27 #include "qgsproject.h"
28 #include "qgsgui.h"
29 #include "qgslayoututils.h"
30 #include <QtTest/QSignalSpy>
31 #include <QSvgGenerator>
32 #include <QPrinter>
33
34 class TestQgsLayoutView: public QObject
35 {
36 Q_OBJECT
37 private slots:
38 void initTestCase(); // will be called before the first testfunction is executed.
39 void cleanupTestCase(); // will be called after the last testfunction was executed.
40 void init(); // will be called before each testfunction is executed.
41 void cleanup(); // will be called after every testfunction.
42 void basic();
43 void tool();
44 void events();
45 void guiRegistry();
46 void rubberBand();
47
48 private:
49
50 };
51
initTestCase()52 void TestQgsLayoutView::initTestCase()
53 {
54
55 }
56
cleanupTestCase()57 void TestQgsLayoutView::cleanupTestCase()
58 {
59 }
60
init()61 void TestQgsLayoutView::init()
62 {
63 }
64
cleanup()65 void TestQgsLayoutView::cleanup()
66 {
67 }
68
basic()69 void TestQgsLayoutView::basic()
70 {
71 QgsProject p;
72 QgsLayout *layout = new QgsLayout( &p );
73 QgsLayoutView *view = new QgsLayoutView();
74
75 QSignalSpy spyLayoutChanged( view, &QgsLayoutView::layoutSet );
76 view->setCurrentLayout( layout );
77 QCOMPARE( view->currentLayout(), layout );
78 QCOMPARE( spyLayoutChanged.count(), 1 );
79
80 delete view;
81 delete layout;
82 }
83
tool()84 void TestQgsLayoutView::tool()
85 {
86 QgsLayoutView *view = new QgsLayoutView();
87 QgsLayoutViewTool *tool = new QgsLayoutViewTool( view, QStringLiteral( "name" ) );
88 QgsLayoutViewTool *tool2 = new QgsLayoutViewTool( view, QStringLiteral( "name2" ) );
89
90 QVERIFY( tool->isClickAndDrag( QPoint( 0, 10 ), QPoint( 5, 10 ) ) );
91 QVERIFY( tool->isClickAndDrag( QPoint( 0, 10 ), QPoint( 5, 15 ) ) );
92 QVERIFY( tool->isClickAndDrag( QPoint( 5, 10 ), QPoint( 5, 15 ) ) );
93 QVERIFY( !tool->isClickAndDrag( QPoint( 0, 10 ), QPoint( 1, 11 ) ) );
94 QVERIFY( !tool->isClickAndDrag( QPoint( 1, 10 ), QPoint( 1, 11 ) ) );
95 QVERIFY( !tool->isClickAndDrag( QPoint( 0, 10 ), QPoint( 1, 10 ) ) );
96 QVERIFY( !tool->isClickAndDrag( QPoint( 0, 10 ), QPoint( 0, 10 ) ) );
97
98 QSignalSpy spySetTool( view, &QgsLayoutView::toolSet );
99 QSignalSpy spyToolActivated( tool, &QgsLayoutViewTool::activated );
100 QSignalSpy spyToolActivated2( tool2, &QgsLayoutViewTool::activated );
101 QSignalSpy spyToolDeactivated( tool, &QgsLayoutViewTool::deactivated );
102 QSignalSpy spyToolDeactivated2( tool2, &QgsLayoutViewTool::deactivated );
103 view->setTool( tool );
104 QCOMPARE( view->tool(), tool );
105 QCOMPARE( spySetTool.count(), 1 );
106 QCOMPARE( spyToolActivated.count(), 1 );
107 QCOMPARE( spyToolDeactivated.count(), 0 );
108
109 view->setTool( tool2 );
110 QCOMPARE( view->tool(), tool2 );
111 QCOMPARE( spySetTool.count(), 2 );
112 QCOMPARE( spyToolActivated.count(), 1 );
113 QCOMPARE( spyToolDeactivated.count(), 1 );
114 QCOMPARE( spyToolActivated2.count(), 1 );
115 QCOMPARE( spyToolDeactivated2.count(), 0 );
116
117 delete tool2;
118 QVERIFY( !view->tool() );
119 QCOMPARE( spySetTool.count(), 3 );
120 QCOMPARE( spyToolActivated.count(), 1 );
121 QCOMPARE( spyToolDeactivated.count(), 1 );
122 QCOMPARE( spyToolActivated2.count(), 1 );
123 QCOMPARE( spyToolDeactivated2.count(), 1 );
124
125 delete view;
126 }
127
128 class LoggingTool : public QgsLayoutViewTool // clazy:exclude=missing-qobject-macro
129 {
130 public:
131
LoggingTool(QgsLayoutView * view)132 LoggingTool( QgsLayoutView *view )
133 : QgsLayoutViewTool( view, QStringLiteral( "logging" ) )
134 {}
135
136 bool receivedMoveEvent = false;
layoutMoveEvent(QgsLayoutViewMouseEvent * event)137 void layoutMoveEvent( QgsLayoutViewMouseEvent *event ) override
138 {
139 receivedMoveEvent = true;
140 QCOMPARE( event->layoutPoint().x(), 8.0 );
141 QCOMPARE( event->layoutPoint().y(), 6.0 );
142 }
143
144 bool receivedDoubleClickEvent = false;
layoutDoubleClickEvent(QgsLayoutViewMouseEvent * event)145 void layoutDoubleClickEvent( QgsLayoutViewMouseEvent *event ) override
146 {
147 receivedDoubleClickEvent = true;
148 QCOMPARE( event->layoutPoint().x(), 8.0 );
149 QCOMPARE( event->layoutPoint().y(), 6.0 );
150 }
151
152 bool receivedPressEvent = false;
layoutPressEvent(QgsLayoutViewMouseEvent * event)153 void layoutPressEvent( QgsLayoutViewMouseEvent *event ) override
154 {
155 receivedPressEvent = true;
156 QCOMPARE( event->layoutPoint().x(), 8.0 );
157 QCOMPARE( event->layoutPoint().y(), 6.0 );
158 }
159
160 bool receivedReleaseEvent = false;
layoutReleaseEvent(QgsLayoutViewMouseEvent * event)161 void layoutReleaseEvent( QgsLayoutViewMouseEvent *event ) override
162 {
163 receivedReleaseEvent = true;
164 QCOMPARE( event->layoutPoint().x(), 8.0 );
165 QCOMPARE( event->layoutPoint().y(), 6.0 );
166 }
167
168 bool receivedWheelEvent = false;
wheelEvent(QWheelEvent *)169 void wheelEvent( QWheelEvent * ) override
170 {
171 receivedWheelEvent = true;
172 }
173
174 bool receivedKeyPressEvent = false;
keyPressEvent(QKeyEvent *)175 void keyPressEvent( QKeyEvent * ) override
176 {
177 receivedKeyPressEvent = true;
178 }
179
180 bool receivedKeyReleaseEvent = false;
keyReleaseEvent(QKeyEvent *)181 void keyReleaseEvent( QKeyEvent * ) override
182 {
183 receivedKeyReleaseEvent = true;
184 }
185 };
186
events()187 void TestQgsLayoutView::events()
188 {
189 QgsProject p;
190 QgsLayoutView *view = new QgsLayoutView();
191 QgsLayout *layout = new QgsLayout( &p );
192 view->setCurrentLayout( layout );
193 layout->setSceneRect( 0, 0, 1000, 1000 );
194 view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
195 view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
196 view->setFrameStyle( 0 );
197 view->resize( 100, 100 );
198 view->setFixedSize( 100, 100 );
199 QCOMPARE( view->width(), 100 );
200 QCOMPARE( view->height(), 100 );
201
202 QTransform transform;
203 transform.scale( 10, 10 );
204 view->setTransform( transform );
205
206 LoggingTool *tool = new LoggingTool( view );
207 view->setTool( tool );
208
209 QPointF point( 80, 60 );
210 QMouseEvent press( QEvent::MouseButtonPress, point,
211 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
212 QMouseEvent move( QEvent::MouseMove, point,
213 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
214 QMouseEvent releases( QEvent::MouseButtonRelease, point,
215 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
216 QMouseEvent dblClick( QEvent::MouseButtonDblClick, point,
217 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
218 QWheelEvent wheelEvent( point, 10,
219 Qt::LeftButton, Qt::NoModifier );
220 QKeyEvent keyPress( QEvent::KeyPress, 10, Qt::NoModifier );
221 QKeyEvent keyRelease( QEvent::KeyRelease, 10, Qt::NoModifier );
222
223 view->mouseMoveEvent( &move );
224 QVERIFY( tool->receivedMoveEvent );
225 view->mousePressEvent( &press );
226 QVERIFY( tool->receivedPressEvent );
227 view->mouseReleaseEvent( &releases );
228 QVERIFY( tool->receivedReleaseEvent );
229 view->mouseDoubleClickEvent( &dblClick );
230 QVERIFY( tool->receivedDoubleClickEvent );
231 view->wheelEvent( &wheelEvent );
232 QVERIFY( tool->receivedWheelEvent );
233 view->keyPressEvent( &keyPress );
234 QVERIFY( tool->receivedKeyPressEvent );
235 view->keyReleaseEvent( &keyRelease );
236 QVERIFY( tool->receivedKeyReleaseEvent );
237 }
238
239 //simple item for testing, since some methods in QgsLayoutItem are pure virtual
240 class TestItem : public QgsLayoutItem // clazy:exclude=missing-qobject-macro
241 {
242 public:
243
TestItem(QgsLayout * layout)244 TestItem( QgsLayout *layout ) : QgsLayoutItem( layout ) {}
245
246 int mFlag = 0;
247
248 //implement pure virtual methods
type() const249 int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; }
draw(QgsLayoutItemRenderContext &)250 void draw( QgsLayoutItemRenderContext & ) override
251 { }
252 };
253
254 QgsLayout *mLayout = nullptr;
255 QString mReport;
256
257 bool renderCheck( QString testName, QImage &image, int mismatchCount );
258
guiRegistry()259 void TestQgsLayoutView::guiRegistry()
260 {
261 // test QgsLayoutItemGuiRegistry
262 QgsLayoutItemGuiRegistry registry;
263
264 // empty registry
265 QVERIFY( !registry.itemMetadata( -1 ) );
266 QVERIFY( registry.itemMetadataIds().isEmpty() );
267 QVERIFY( !registry.createItemWidget( nullptr ) );
268 QVERIFY( !registry.createItemWidget( nullptr ) );
269 std::unique_ptr< TestItem > testItem = qgis::make_unique< TestItem >( nullptr );
270 QVERIFY( !registry.createItemWidget( testItem.get() ) ); // not in registry
271
272 QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemGuiRegistry::typeAdded );
273
274 // add a dummy item to registry
275 auto createWidget = []( QgsLayoutItem * item )->QgsLayoutItemBaseWidget *
276 {
277 return new QgsLayoutItemBaseWidget( nullptr, item );
278 };
279
280 auto createRubberBand = []( QgsLayoutView * view )->QgsLayoutViewRubberBand *
281 {
282 return new QgsLayoutViewRectangularRubberBand( view );
283 };
284
285 QgsLayoutItemGuiMetadata *metadata = new QgsLayoutItemGuiMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "mytype" ), QIcon(), createWidget, createRubberBand );
286 QVERIFY( registry.addLayoutItemGuiMetadata( metadata ) );
287 QCOMPARE( spyTypeAdded.count(), 1 );
288 int uuid = registry.itemMetadataIds().value( 0 );
289 QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), uuid );
290
291 // duplicate type id is allowed
292 metadata = new QgsLayoutItemGuiMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "mytype" ), QIcon(), createWidget, createRubberBand );
293 QVERIFY( registry.addLayoutItemGuiMetadata( metadata ) );
294 QCOMPARE( spyTypeAdded.count(), 2 );
295 //retrieve metadata
296 QVERIFY( !registry.itemMetadata( -1 ) );
297 QCOMPARE( registry.itemMetadataIds().count(), 2 );
298
299 QVERIFY( registry.itemMetadata( uuid ) );
300 QCOMPARE( registry.itemMetadata( uuid )->visibleName(), QStringLiteral( "mytype" ) );
301
302 QWidget *widget = registry.createItemWidget( testItem.get() );
303 QVERIFY( widget );
304 delete widget;
305
306 QgsLayoutView *view = new QgsLayoutView();
307 //should use metadata's method
308 QgsLayoutViewRubberBand *band = registry.createItemRubberBand( uuid, view );
309 QVERIFY( band );
310 QVERIFY( dynamic_cast< QgsLayoutViewRectangularRubberBand * >( band ) );
311 QCOMPARE( band->view(), view );
312 delete band;
313
314 // groups
315 QVERIFY( registry.addItemGroup( QgsLayoutItemGuiGroup( QStringLiteral( "g1" ) ) ) );
316 QCOMPARE( registry.itemGroup( QStringLiteral( "g1" ) ).id, QStringLiteral( "g1" ) );
317 // can't add duplicate group
318 QVERIFY( !registry.addItemGroup( QgsLayoutItemGuiGroup( QStringLiteral( "g1" ) ) ) );
319
320 //creating item
321 QgsLayoutItem *item = registry.createItem( uuid, nullptr );
322 QVERIFY( !item );
323 QgsApplication::layoutItemRegistry()->addLayoutItemType( new QgsLayoutItemMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "my type" ), QStringLiteral( "my types" ), []( QgsLayout * layout )->QgsLayoutItem*
324 {
325 return new TestItem( layout );
326 } ) );
327
328 item = registry.createItem( uuid, nullptr );
329 QVERIFY( item );
330 QCOMPARE( item->type(), QgsLayoutItemRegistry::LayoutItem + 101 );
331 QCOMPARE( static_cast< TestItem * >( item )->mFlag, 0 );
332 delete item;
333
334 // override create func
335 metadata = new QgsLayoutItemGuiMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "mytype" ), QIcon(), createWidget, createRubberBand );
336 metadata->setItemCreationFunction( []( QgsLayout * layout )->QgsLayoutItem*
337 {
338 TestItem *item = new TestItem( layout );
339 item->mFlag = 2;
340 return item;
341 } );
342 QVERIFY( registry.addLayoutItemGuiMetadata( metadata ) );
343 uuid = spyTypeAdded.at( spyTypeAdded.count() - 1 ).at( 0 ).toInt();
344 item = registry.createItem( uuid, nullptr );
345 QVERIFY( item );
346 QCOMPARE( item->type(), QgsLayoutItemRegistry::LayoutItem + 101 );
347 QCOMPARE( static_cast< TestItem * >( item )->mFlag, 2 );
348 delete item;
349
350 }
351
rubberBand()352 void TestQgsLayoutView::rubberBand()
353 {
354 QgsLayoutViewRectangularRubberBand band;
355 band.setBrush( QBrush( QColor( 255, 0, 0 ) ) );
356 QCOMPARE( band.brush().color(), QColor( 255, 0, 0 ) );
357 band.setPen( QPen( QColor( 0, 255, 0 ) ) );
358 QCOMPARE( band.pen().color(), QColor( 0, 255, 0 ) );
359 }
360
361 QGSTEST_MAIN( TestQgsLayoutView )
362 #include "testqgslayoutview.moc"
363