Start work on node edit tool

This commit is contained in:
Nyall Dawson 2017-10-17 18:52:37 +10:00
parent d92b067b28
commit ab7bb7235a
11 changed files with 362 additions and 6 deletions

View File

@ -296,6 +296,7 @@
%Include layout/qgslayoutviewtool.sip
%Include layout/qgslayoutviewtooladditem.sip
%Include layout/qgslayoutviewtooladdnodeitem.sip
%Include layout/qgslayoutviewtooleditnodes.sip
%Include layout/qgslayoutviewtoolpan.sip
%Include layout/qgslayoutviewtoolselect.sip
%Include layout/qgslayoutviewtooltemporarykeypan.sip

View File

@ -0,0 +1,49 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layout/qgslayoutviewtooleditnodes.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsLayoutViewToolEditNodes : QgsLayoutViewTool
{
%Docstring
Layout view tool for edit node based items in the layout.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgslayoutviewtooleditnodes.h"
%End
public:
QgsLayoutViewToolEditNodes( QgsLayoutView *view /TransferThis/ );
%Docstring
Constructor for QgsLayoutViewToolEditNodes.
%End
virtual void activate();
virtual void layoutPressEvent( QgsLayoutViewMouseEvent *event );
virtual void layoutMoveEvent( QgsLayoutViewMouseEvent *event );
virtual void layoutReleaseEvent( QgsLayoutViewMouseEvent *event );
virtual void deactivate();
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layout/qgslayoutviewtooleditnodes.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -28,6 +28,7 @@
#include "qgslayoutviewtoolpan.h"
#include "qgslayoutviewtoolzoom.h"
#include "qgslayoutviewtoolselect.h"
#include "qgslayoutviewtooleditnodes.h"
#include "qgslayoutitemwidget.h"
#include "qgsgui.h"
#include "qgslayoutitemguiregistry.h"
@ -245,6 +246,11 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mAddItemTool, &QgsLayoutViewToolAddItem::createdItem, this, [ = ] { mView->setTool( mSelectTool ); } );
connect( mAddNodeItemTool, &QgsLayoutViewToolAddNodeItem::createdItem, this, [ = ] { mView->setTool( mSelectTool ); } );
mNodesTool = new QgsLayoutViewToolEditNodes( mView );
mNodesTool->setAction( mActionEditNodesItem );
mToolsActionGroup->addAction( mActionEditNodesItem );
connect( mActionEditNodesItem, &QAction::triggered, mNodesTool, [ = ] { mView->setTool( mNodesTool ); } );
//Ctrl+= should also trigger zoom in
QShortcut *ctrlEquals = new QShortcut( QKeySequence( QStringLiteral( "Ctrl+=" ) ), this );
connect( ctrlEquals, &QShortcut::activated, mActionZoomIn, &QAction::trigger );

View File

@ -28,6 +28,7 @@ class QgsLayoutViewToolAddNodeItem;
class QgsLayoutViewToolPan;
class QgsLayoutViewToolZoom;
class QgsLayoutViewToolSelect;
class QgsLayoutViewToolEditNodes;
class QgsLayoutRuler;
class QComboBox;
class QSlider;
@ -275,6 +276,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
QgsLayoutViewToolPan *mPanTool = nullptr;
QgsLayoutViewToolZoom *mZoomTool = nullptr;
QgsLayoutViewToolSelect *mSelectTool = nullptr;
QgsLayoutViewToolEditNodes *mNodesTool = nullptr;
QMap< QString, QToolButton * > mItemGroupToolButtons;
QMap< QString, QMenu * > mItemGroupSubmenus;

View File

@ -418,7 +418,7 @@ void QgsLayoutItem::attemptMove( const QgsLayoutPoint &p, bool useReferencePoint
void QgsLayoutItem::attemptSetSceneRect( const QRectF &rect, bool includesFrame )
{
QPointF newPos = mapToScene( rect.topLeft() );
QPointF newPos = rect.topLeft();
blockSignals( true );
// translate new size to current item units

View File

@ -150,7 +150,9 @@ bool QgsLayoutNodesItem::addNode( QPointF pt,
void QgsLayoutNodesItem::drawNodes( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle ) const
{
double rectSize = 3.0 / itemStyle->matrix.m11();
context.painter()->setRenderHint( QPainter::Antialiasing, false );
double rectSize = 9.0 / itemStyle->matrix.m11();
QgsStringMap properties;
properties.insert( QStringLiteral( "name" ), QStringLiteral( "cross" ) );
@ -163,7 +165,7 @@ void QgsLayoutNodesItem::drawNodes( QgsRenderContext &context, const QStyleOptio
symbol->startRender( context );
for ( QPointF pt : mPolygon )
symbol->renderPoint( pt, nullptr, context );
symbol->renderPoint( pt * itemStyle->matrix.m11(), nullptr, context );
symbol->stopRender( context );
if ( mSelectedNode >= 0 && mSelectedNode < mPolygon.size() )
@ -172,7 +174,7 @@ void QgsLayoutNodesItem::drawNodes( QgsRenderContext &context, const QStyleOptio
void QgsLayoutNodesItem::drawSelectedNode( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle ) const
{
double rectSize = 3.0 / itemStyle->matrix.m11();
double rectSize = 9.0 / itemStyle->matrix.m11();
QgsStringMap properties;
properties.insert( QStringLiteral( "name" ), QStringLiteral( "square" ) );
@ -185,7 +187,7 @@ void QgsLayoutNodesItem::drawSelectedNode( QgsRenderContext &context, const QSty
symbol->setSize( rectSize );
symbol->startRender( context );
symbol->renderPoint( mPolygon.at( mSelectedNode ), nullptr, context );
symbol->renderPoint( mPolygon.at( mSelectedNode ) * itemStyle->matrix.m11(), nullptr, context );
symbol->stopRender( context );
}
@ -317,7 +319,6 @@ void QgsLayoutNodesItem::updateSceneRect()
// update
prepareGeometryChange();
update();
emit sizePositionChanged();
}

View File

@ -171,6 +171,7 @@ SET(QGIS_GUI_SRCS
layout/qgslayoutviewtool.cpp
layout/qgslayoutviewtooladditem.cpp
layout/qgslayoutviewtooladdnodeitem.cpp
layout/qgslayoutviewtooleditnodes.cpp
layout/qgslayoutviewtoolpan.cpp
layout/qgslayoutviewtoolselect.cpp
layout/qgslayoutviewtooltemporarykeypan.cpp
@ -675,6 +676,7 @@ SET(QGIS_GUI_MOC_HDRS
layout/qgslayoutviewtool.h
layout/qgslayoutviewtooladditem.h
layout/qgslayoutviewtooladdnodeitem.h
layout/qgslayoutviewtooleditnodes.h
layout/qgslayoutviewtoolpan.h
layout/qgslayoutviewtoolselect.h
layout/qgslayoutviewtooltemporarykeypan.h

View File

@ -0,0 +1,168 @@
/***************************************************************************
qgslayoutviewtooleditnodes.cpp
---------------------------
Date : July 2017
Copyright : (C) 2017 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 "qgslayoutviewtooleditnodes.h"
#include "qgslayoutviewmouseevent.h"
#include "qgslayoutview.h"
#include "qgslayout.h"
#include "qgslayoutitemnodeitem.h"
QgsLayoutViewToolEditNodes::QgsLayoutViewToolEditNodes( QgsLayoutView *view )
: QgsLayoutViewTool( view, tr( "Select" ) )
{
setCursor( Qt::CrossCursor );
setFlags( QgsLayoutViewTool::FlagSnaps );
}
void QgsLayoutViewToolEditNodes::activate()
{
displayNodes( true );
QgsLayoutViewTool::activate();
}
void QgsLayoutViewToolEditNodes::layoutPressEvent( QgsLayoutViewMouseEvent *event )
{
if ( event->button() != Qt::LeftButton )
{
event->ignore();
return;
}
const QList<QGraphicsItem *> itemsAtCursorPos = view()->items( event->pos().x(), event->pos().y(),
mMoveContentSearchRadius,
mMoveContentSearchRadius );
if ( itemsAtCursorPos.isEmpty() )
return;
mNodesItemIndex = -1;
mNodesItem = nullptr;
isMoving = false;
for ( QGraphicsItem *graphicsItem : itemsAtCursorPos )
{
QgsLayoutNodesItem *item = dynamic_cast<QgsLayoutNodesItem *>( graphicsItem );
if ( item && !item->isLocked() )
{
int index = item->nodeAtPosition( event->layoutPoint() );
if ( index != -1 )
{
mNodesItemIndex = index;
mNodesItem = item;
mMoveContentStartPos = event->layoutPoint();
}
}
if ( mNodesItemIndex != -1 )
{
layout()->undoStack()->beginCommand( mNodesItem, tr( "Move Item Node" ) );
setSelectedNode( mNodesItem, mNodesItemIndex );
isMoving = true;
break;
}
}
}
void QgsLayoutViewToolEditNodes::layoutMoveEvent( QgsLayoutViewMouseEvent *event )
{
if ( !isMoving )
{
event->ignore();
return;
}
if ( mNodesItemIndex != -1 && event->layoutPoint() != mMoveContentStartPos )
{
mNodesItem->moveNode( mNodesItemIndex, event->snappedPoint() );
}
}
void QgsLayoutViewToolEditNodes::layoutReleaseEvent( QgsLayoutViewMouseEvent *event )
{
if ( event->button() != Qt::LeftButton || !isMoving )
{
event->ignore();
return;
}
isMoving = false;
if ( mNodesItemIndex != -1 )
{
if ( event->layoutPoint() != mMoveContentStartPos )
{
layout()->undoStack()->endCommand();
}
else
{
layout()->undoStack()->cancelCommand();
}
}
}
void QgsLayoutViewToolEditNodes::deactivate()
{
displayNodes( false );
deselectNodes();
QgsLayoutViewTool::deactivate();
}
void QgsLayoutViewToolEditNodes::displayNodes( bool display )
{
QList<QgsLayoutNodesItem *> nodesShapes;
layout()->layoutItems( nodesShapes );
for ( QgsLayoutNodesItem *item : qgis::as_const( nodesShapes ) )
{
item->setDisplayNodes( display );
}
layout()->update();
}
void QgsLayoutViewToolEditNodes::deselectNodes()
{
QList<QgsLayoutNodesItem *> nodesShapes;
layout()->layoutItems( nodesShapes );
for ( QgsLayoutNodesItem *item : qgis::as_const( nodesShapes ) )
{
item->deselectNode();
}
layout()->update();
}
void QgsLayoutViewToolEditNodes::setSelectedNode( QgsLayoutNodesItem *shape, int index )
{
QList<QgsLayoutNodesItem *> nodesShapes;
layout()->layoutItems( nodesShapes );
for ( QgsLayoutNodesItem *item : qgis::as_const( nodesShapes ) )
{
if ( item == shape )
{
item->setSelectedNode( index );
layout()->setSelectedItem( item );
item->update();
}
else
{
item->deselectNode();
item->update();
}
}
}

View File

@ -0,0 +1,68 @@
/***************************************************************************
qgslayoutviewtooleditnodes.h
-------------------------
Date : October 2017
Copyright : (C) 2017 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. *
* *
***************************************************************************/
#ifndef QGSLAYOUTVIEWTOOLEDITNODES_H
#define QGSLAYOUTVIEWTOOLEDITNODES_H
#include "qgis.h"
#include "qgis_gui.h"
#include "qgslayoutviewtool.h"
class QgsLayoutNodesItem;
/**
* \ingroup gui
* Layout view tool for edit node based items in the layout.
* \since QGIS 3.0
*/
class GUI_EXPORT QgsLayoutViewToolEditNodes : public QgsLayoutViewTool
{
Q_OBJECT
public:
/**
* Constructor for QgsLayoutViewToolEditNodes.
*/
QgsLayoutViewToolEditNodes( QgsLayoutView *view SIP_TRANSFERTHIS );
void activate() override;
void layoutPressEvent( QgsLayoutViewMouseEvent *event ) override;
void layoutMoveEvent( QgsLayoutViewMouseEvent *event ) override;
void layoutReleaseEvent( QgsLayoutViewMouseEvent *event ) override;
void deactivate() override;
private:
const double mMoveContentSearchRadius = 25;
QgsLayoutNodesItem *mNodesItem = nullptr;
int mNodesItemIndex = -1;
//! Start position of content move
QPointF mMoveContentStartPos;
bool isMoving = false;
void displayNodes( bool display = true );
void deselectNodes();
void setSelectedNode( QgsLayoutNodesItem *shape, int index );
};
#endif // QGSLAYOUTVIEWTOOLEDITNODES_H

View File

@ -76,6 +76,7 @@
<addaction name="mActionPan"/>
<addaction name="mActionZoomTool"/>
<addaction name="mActionSelectMoveItem"/>
<addaction name="mActionEditNodesItem"/>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
@ -1009,6 +1010,21 @@
<string>F5</string>
</property>
</action>
<action name="mActionEditNodesItem">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionNodeTool.svg</normaloff>:/images/themes/default/mActionNodeTool.svg</iconset>
</property>
<property name="text">
<string>Edit Nodes Item</string>
</property>
<property name="toolTip">
<string>Edit Nodes Item</string>
</property>
</action>
</widget>
<resources>
<include location="../../../images/images.qrc"/>

View File

@ -155,6 +155,7 @@ class TestQgsLayoutItem: public QObject
void blendMode();
void opacity();
void excludeFromExports();
void setSceneRect();
private:
@ -1191,6 +1192,48 @@ void TestQgsLayoutItem::move()
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::rotation()
{
QgsProject proj;