Skip to content

Commit 72a1f77

Browse files
committed
After a undo/redo action occurs, select the affected items
1 parent 48a45b3 commit 72a1f77

File tree

9 files changed

+137
-6
lines changed

9 files changed

+137
-6
lines changed

python/core/layout/qgslayoutundostack.sip

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111

1212

13-
class QgsLayoutUndoStack
13+
14+
class QgsLayoutUndoStack : QObject
1415
{
1516
%Docstring
1617
An undo stack for QgsLayouts.
@@ -86,6 +87,19 @@ class QgsLayoutUndoStack
8687
:rtype: QUndoStack
8788
%End
8889

90+
void notifyUndoRedoOccurred( QgsLayoutItem *item );
91+
%Docstring
92+
Notifies the stack that an undo or redo action occurred for a specified ``item``.
93+
%End
94+
95+
signals:
96+
97+
void undoRedoOccurredForItems( const QSet< QString > itemUuids );
98+
%Docstring
99+
Emitted when an undo or redo action has occurred, which affected a
100+
set of layout ``itemUuids``.
101+
%End
102+
89103
private:
90104
QgsLayoutUndoStack( const QgsLayoutUndoStack &other );
91105
};

src/app/layout/qgslayoutdesignerdialog.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ void QgsLayoutDesignerDialog::setCurrentLayout( QgsLayout *layout )
581581
mLayoutToolbar->addAction( mUndoAction );
582582
mLayoutToolbar->addAction( mRedoAction );
583583

584+
connect( mLayout->undoStack(), &QgsLayoutUndoStack::undoRedoOccurredForItems, this, &QgsLayoutDesignerDialog::undoRedoOccurredForItems );
584585
connect( mActionClearGuides, &QAction::triggered, &mLayout->guides(), [ = ]
585586
{
586587
mLayout->guides().clear();
@@ -620,6 +621,9 @@ void QgsLayoutDesignerDialog::setIconSizes( int size )
620621

621622
void QgsLayoutDesignerDialog::showItemOptions( QgsLayoutItem *item, bool bringPanelToFront )
622623
{
624+
if ( mBlockItemOptions )
625+
return;
626+
623627
if ( !item )
624628
{
625629
delete mItemPropertiesStack->takeMainPanel();
@@ -1073,6 +1077,27 @@ void QgsLayoutDesignerDialog::dockVisibilityChanged( bool visible )
10731077
}
10741078
}
10751079

1080+
void QgsLayoutDesignerDialog::undoRedoOccurredForItems( const QSet<QString> itemUuids )
1081+
{
1082+
mBlockItemOptions = true;
1083+
1084+
mLayout->deselectAll();
1085+
QgsLayoutItem *focusItem = nullptr;
1086+
for ( const QString &uuid : itemUuids )
1087+
{
1088+
QgsLayoutItem *item = mLayout->itemByUuid( uuid );
1089+
if ( !item )
1090+
continue;
1091+
1092+
item->setSelected( true );
1093+
focusItem = item;
1094+
}
1095+
mBlockItemOptions = false;
1096+
1097+
if ( focusItem )
1098+
showItemOptions( focusItem );
1099+
}
1100+
10761101
QgsLayoutView *QgsLayoutDesignerDialog::view()
10771102
{
10781103
return mView;

src/app/layout/qgslayoutdesignerdialog.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
245245
void addPages();
246246
void statusMessageReceived( const QString &message );
247247
void dockVisibilityChanged( bool visible );
248+
void undoRedoOccurredForItems( const QSet< QString > itemUuids );
248249

249250
private:
250251

@@ -312,6 +313,8 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
312313
};
313314
QMap< QString, PanelStatus > mPanelStatus;
314315

316+
bool mBlockItemOptions = false;
317+
315318
//! Save window state
316319
void saveWindowState();
317320

src/core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ SET(QGIS_CORE_MOC_HDRS
742742
layout/qgslayoutmodel.h
743743
layout/qgslayoutpagecollection.h
744744
layout/qgslayoutobject.h
745+
layout/qgslayoutundostack.h
745746

746747
symbology/qgscptcityarchive.h
747748
symbology/qgssvgcache.h
@@ -1001,7 +1002,6 @@ SET(QGIS_CORE_HDRS
10011002
layout/qgslayoutsize.h
10021003
layout/qgslayoutsnapper.h
10031004
layout/qgslayoutundocommand.h
1004-
layout/qgslayoutundostack.h
10051005
layout/qgslayoututils.h
10061006

10071007
metadata/qgslayermetadata.h

src/core/layout/qgslayoutitempicture.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ QString QgsLayoutItemPicture::picturePath() const
682682
return mSourcePath;
683683
}
684684

685-
bool QgsLayoutItemPicture::writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
685+
bool QgsLayoutItemPicture::writePropertiesToElement( QDomElement &elem, QDomDocument &, const QgsReadWriteContext &context ) const
686686
{
687687
QString imagePath = mSourcePath;
688688

src/core/layout/qgslayoutitemundocommand.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ void QgsLayoutItemUndoCommand::restoreState( QDomDocument &stateDoc )
7272

7373
item->readXml( stateDoc.documentElement().firstChild().toElement(), stateDoc, QgsReadWriteContext() );
7474
mLayout->project()->setDirty( true );
75+
mLayout->undoStack()->notifyUndoRedoOccurred( item );
7576
}
7677

7778
QgsLayoutItem *QgsLayoutItemUndoCommand::recreateItem( int itemType, QgsLayout *layout )

src/core/layout/qgslayoutundostack.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
#include <QUndoStack>
2222

2323
QgsLayoutUndoStack::QgsLayoutUndoStack( QgsLayout *layout )
24-
: mLayout( layout )
24+
: QObject()
25+
, mLayout( layout )
2526
, mUndoStack( new QUndoStack( layout ) )
2627
{
27-
28+
connect( mUndoStack.get(), &QUndoStack::indexChanged, this, &QgsLayoutUndoStack::indexChanged );
2829
}
2930

3031
void QgsLayoutUndoStack::beginMacro( const QString &commandText )
@@ -76,3 +77,17 @@ QUndoStack *QgsLayoutUndoStack::stack()
7677
return mUndoStack.get();
7778

7879
}
80+
81+
void QgsLayoutUndoStack::notifyUndoRedoOccurred( QgsLayoutItem *item )
82+
{
83+
mUndoRedoOccurredItemUuids.insert( item->uuid() );
84+
}
85+
86+
void QgsLayoutUndoStack::indexChanged()
87+
{
88+
if ( mUndoRedoOccurredItemUuids.empty() )
89+
return;
90+
91+
emit undoRedoOccurredForItems( mUndoRedoOccurredItemUuids );
92+
mUndoRedoOccurredItemUuids.clear();
93+
}

src/core/layout/qgslayoutundostack.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "qgis.h"
2222
#include "qgis_core.h"
2323
#include "qgslayoutundocommand.h"
24+
#include "qgslayoutitem.h"
25+
2426
#include <memory>
2527

2628
class QgsLayout;
@@ -31,8 +33,10 @@ class QUndoStack;
3133
* An undo stack for QgsLayouts.
3234
* \since QGIS 3.0
3335
*/
34-
class CORE_EXPORT QgsLayoutUndoStack
36+
class CORE_EXPORT QgsLayoutUndoStack : public QObject
3537
{
38+
Q_OBJECT
39+
3640
public:
3741

3842
/**
@@ -98,6 +102,23 @@ class CORE_EXPORT QgsLayoutUndoStack
98102
*/
99103
QUndoStack *stack();
100104

105+
/**
106+
* Notifies the stack that an undo or redo action occurred for a specified \a item.
107+
*/
108+
void notifyUndoRedoOccurred( QgsLayoutItem *item );
109+
110+
signals:
111+
112+
/**
113+
* Emitted when an undo or redo action has occurred, which affected a
114+
* set of layout \a itemUuids.
115+
*/
116+
void undoRedoOccurredForItems( const QSet< QString > itemUuids );
117+
118+
private slots:
119+
120+
void indexChanged();
121+
101122
private:
102123

103124
QgsLayout *mLayout = nullptr;
@@ -106,6 +127,8 @@ class CORE_EXPORT QgsLayoutUndoStack
106127

107128
std::vector< std::unique_ptr< QgsAbstractLayoutUndoCommand > > mActiveCommands;
108129

130+
QSet< QString > mUndoRedoOccurredItemUuids;
131+
109132
#ifdef SIP_RUN
110133
QgsLayoutUndoStack( const QgsLayoutUndoStack &other );
111134
#endif

tests/src/core/testqgslayout.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class TestQgsLayout: public QObject
4141
void addItem();
4242
void layoutItems();
4343
void layoutItemByUuid();
44+
void undoRedoOccurred();
4445

4546
private:
4647
QString mReport;
@@ -419,6 +420,55 @@ void TestQgsLayout::layoutItemByUuid()
419420
QCOMPARE( l.itemByUuid( map1->uuid() ), map1 );
420421
}
421422

423+
void TestQgsLayout::undoRedoOccurred()
424+
{
425+
// test emitting undo/redo occurred signal
426+
QgsProject proj;
427+
QgsLayout l( &proj );
428+
429+
QSignalSpy spyOccurred( l.undoStack(), &QgsLayoutUndoStack::undoRedoOccurredForItems );
430+
431+
QgsLayoutItemShape *item = new QgsLayoutItemShape( &l );
432+
l.addLayoutItem( item );
433+
434+
QCOMPARE( spyOccurred.count(), 0 );
435+
//adds a new undo command
436+
item->setId( "test" );
437+
QCOMPARE( spyOccurred.count(), 0 );
438+
439+
QgsLayoutItemShape *item2 = new QgsLayoutItemShape( &l );
440+
l.addLayoutItem( item2 );
441+
item2->setId( "test2" );
442+
QCOMPARE( spyOccurred.count(), 0 );
443+
444+
l.undoStack()->stack()->undo();
445+
QCOMPARE( spyOccurred.count(), 1 );
446+
QSet< QString > items = qvariant_cast< QSet< QString > >( spyOccurred.at( 0 ).at( 0 ) );
447+
QCOMPARE( items, QSet< QString >() << item2->uuid() );
448+
449+
l.undoStack()->stack()->redo();
450+
QCOMPARE( spyOccurred.count(), 2 );
451+
items = qvariant_cast< QSet< QString> >( spyOccurred.at( 1 ).at( 0 ) );
452+
QCOMPARE( items, QSet< QString >() << item2->uuid() );
453+
454+
// macro undo
455+
l.undoStack()->beginMacro( QString() );
456+
item->setId( "new id" );
457+
item2->setId( "new id2" );
458+
l.undoStack()->endMacro();
459+
QCOMPARE( spyOccurred.count(), 2 );
460+
461+
l.undoStack()->stack()->undo();
462+
QCOMPARE( spyOccurred.count(), 3 );
463+
items = qvariant_cast< QSet< QString > >( spyOccurred.at( 2 ).at( 0 ) );
464+
QCOMPARE( items, QSet< QString >() << item->uuid() << item2->uuid() );
465+
l.undoStack()->stack()->redo();
466+
QCOMPARE( spyOccurred.count(), 4 );
467+
items = qvariant_cast< QSet< QString > >( spyOccurred.at( 3 ).at( 0 ) );
468+
QCOMPARE( items, QSet< QString >() << item->uuid() << item2->uuid() );
469+
470+
}
471+
422472

423473
QGSTEST_MAIN( TestQgsLayout )
424474
#include "testqgslayout.moc"

0 commit comments

Comments
 (0)