Skip to content

Commit

Permalink
Port select next above/below actions
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Oct 6, 2017
1 parent dbb3125 commit 4cba2b9
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 0 deletions.
18 changes: 18 additions & 0 deletions python/gui/layout/qgslayoutview.sip
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ class QgsLayoutView: QGraphicsView
Selects all items in the view.
.. seealso:: deselectAll()
.. seealso:: invertSelection()
.. seealso:: selectNextItemAbove()
.. seealso:: selectNextItemBelow()
%End

void deselectAll();
Expand All @@ -192,6 +194,22 @@ class QgsLayoutView: QGraphicsView
and deselecting and selected items.
.. seealso:: selectAll()
.. seealso:: deselectAll()
%End

void selectNextItemAbove();
%Docstring
Selects the next item above the existing selection, by item z order.
.. seealso:: selectNextItemBelow()
.. seealso:: selectAll()
.. seealso:: deselectAll()
%End

void selectNextItemBelow();
%Docstring
Selects the next item below the existing selection, by item z order.
.. seealso:: selectNextItemAbove()
.. seealso:: selectAll()
.. seealso:: deselectAll()
%End

void viewChanged();
Expand Down
21 changes: 21 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,14 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionSelectAll, &QAction::triggered, mView, &QgsLayoutView::selectAll );
connect( mActionDeselectAll, &QAction::triggered, mView, &QgsLayoutView::deselectAll );
connect( mActionInvertSelection, &QAction::triggered, mView, &QgsLayoutView::invertSelection );
connect( mActionSelectNextAbove, &QAction::triggered, mView, &QgsLayoutView::selectNextItemAbove );
connect( mActionSelectNextBelow, &QAction::triggered, mView, &QgsLayoutView::selectNextItemBelow );

connect( mActionAddPages, &QAction::triggered, this, &QgsLayoutDesignerDialog::addPages );

connect( mActionUnlockAll, &QAction::triggered, this, &QgsLayoutDesignerDialog::unlockAllItems );
connect( mActionLockItems, &QAction::triggered, this, &QgsLayoutDesignerDialog::lockSelectedItems );

//create status bar labels
mStatusCursorXLabel = new QLabel( mStatusBar );
mStatusCursorXLabel->setMinimumWidth( 100 );
Expand Down Expand Up @@ -477,6 +482,22 @@ void QgsLayoutDesignerDialog::snapToItems( bool enabled )
mLayout->snapper().setSnapToItems( enabled );
}

void QgsLayoutDesignerDialog::unlockAllItems()
{
if ( mLayout )
{
mLayout->unlockAllItems();
}
}

void QgsLayoutDesignerDialog::lockSelectedItems()
{
if ( mLayout )
{
mLayout->lockSelectedItems();
}
}

void QgsLayoutDesignerDialog::closeEvent( QCloseEvent * )
{
emit aboutToClose();
Expand Down
12 changes: 12 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
*/
void snapToItems( bool enabled );

/**
* Unlocks all locked items in the layout.
* \see lockSelectedItems()
*/
void unlockAllItems();

/**
* Locks any selected items in the layout.
* \see unlockAllItems()
*/
void lockSelectedItems();

signals:

/**
Expand Down
45 changes: 45 additions & 0 deletions src/gui/layout/qgslayoutview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "qgslayoutviewtooltemporarymousepan.h"
#include "qgslayoutmousehandles.h"
#include "qgslayoutruler.h"
#include "qgslayoutmodel.h"
#include "qgssettings.h"
#include "qgsrectangle.h"
#include "qgsapplication.h"
Expand Down Expand Up @@ -353,6 +354,50 @@ void QgsLayoutView::invertSelection()
emit itemFocused( focusedItem );
}


void selectNextByZOrder( QgsLayout *layout, bool above )
{
if ( !layout )
return;

QgsLayoutItem *previousSelectedItem = nullptr;
const QList<QgsLayoutItem *> selectedItems = layout->selectedLayoutItems();
if ( !selectedItems.isEmpty() )
{
previousSelectedItem = selectedItems.at( 0 );
}

if ( !previousSelectedItem )
{
return;
}

//select item with target z value
QgsLayoutItem *selectedItem = nullptr;
if ( !above )
selectedItem = layout->itemsModel()->findItemBelow( previousSelectedItem );
else
selectedItem = layout->itemsModel()->findItemAbove( previousSelectedItem );

if ( !selectedItem )
{
return;
}

//OK, found a good target item
layout->setSelectedItem( selectedItem );
}

void QgsLayoutView::selectNextItemAbove()
{
selectNextByZOrder( currentLayout(), true );
}

void QgsLayoutView::selectNextItemBelow()
{
selectNextByZOrder( currentLayout(), false );
}

void QgsLayoutView::mousePressEvent( QMouseEvent *event )
{
mSnapMarker->setVisible( false );
Expand Down
18 changes: 18 additions & 0 deletions src/gui/layout/qgslayoutview.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
* Selects all items in the view.
* \see deselectAll()
* \see invertSelection()
* \see selectNextItemAbove()
* \see selectNextItemBelow()
*/
void selectAll();

Expand All @@ -237,6 +239,22 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
*/
void invertSelection();

/**
* Selects the next item above the existing selection, by item z order.
* \see selectNextItemBelow()
* \see selectAll()
* \see deselectAll()
*/
void selectNextItemAbove();

/**
* Selects the next item below the existing selection, by item z order.
* \see selectNextItemAbove()
* \see selectAll()
* \see deselectAll()
*/
void selectNextItemBelow();

/**
* Updates associated rulers and other widgets after view extent or zoom has changed.
* This should be called after calling any of the QGraphicsView
Expand Down
48 changes: 48 additions & 0 deletions src/ui/layout/qgslayoutdesignerbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,17 @@
<addaction name="mActionSelectNextBelow"/>
<addaction name="mActionSelectNextAbove"/>
</widget>
<widget class="QMenu" name="menuLayout">
<property name="title">
<string>Layout</string>
</property>
<addaction name="mActionLockItems"/>
<addaction name="mActionUnlockAll"/>
</widget>
<addaction name="mLayoutMenu"/>
<addaction name="menuEdit"/>
<addaction name="mMenuView"/>
<addaction name="menuLayout"/>
<addaction name="mItemMenu"/>
</widget>
<widget class="QToolBar" name="mNavigationToolbar">
Expand All @@ -171,6 +179,19 @@
<addaction name="mActionZoomActual"/>
<addaction name="mActionZoomAll"/>
</widget>
<widget class="QToolBar" name="mActionsToolbar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="mActionLockItems"/>
<addaction name="mActionUnlockAll"/>
</widget>
<action name="mActionClose">
<property name="text">
<string>&amp;Close</string>
Expand Down Expand Up @@ -536,6 +557,33 @@
<string>Ctrl+Alt+]</string>
</property>
</action>
<action name="mActionLockItems">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/locked.svg</normaloff>:/images/themes/default/locked.svg</iconset>
</property>
<property name="text">
<string>Loc&amp;k Selected Items</string>
</property>
<property name="shortcut">
<string>Ctrl+L</string>
</property>
</action>
<action name="mActionUnlockAll">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/unlocked.svg</normaloff>:/images/themes/default/unlocked.svg</iconset>
</property>
<property name="text">
<string>Unl&amp;ock All</string>
</property>
<property name="toolTip">
<string>Unlock All Items</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+L</string>
</property>
</action>
</widget>
<resources>
<include location="../../../images/images.qrc"/>
Expand Down
72 changes: 72 additions & 0 deletions tests/src/python/test_qgslayoutview.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,78 @@ def testInvertSelection(self):
self.assertFalse(item2.isSelected())
self.assertFalse(item3.isSelected()) # locked

def testSelectNextByZOrder(self):
p = QgsProject()
l = QgsLayout(p)

# add some items
item1 = QgsLayoutItemMap(l)
l.addItem(item1)
item2 = QgsLayoutItemMap(l)
l.addItem(item2)
item3 = QgsLayoutItemMap(l)
item3.setLocked(True)
l.addItem(item3)

view = QgsLayoutView()
# no layout, no crash
view.selectNextItemAbove()
view.selectNextItemBelow()

view.setCurrentLayout(l)

focused_item_spy = QSignalSpy(view.itemFocused)

# no selection
view.selectNextItemAbove()
view.selectNextItemBelow()
self.assertEqual(len(focused_item_spy), 0)

l.setSelectedItem(item1)
self.assertEqual(len(focused_item_spy), 1)
# already bottom most
view.selectNextItemBelow()
self.assertTrue(item1.isSelected())
self.assertFalse(item2.isSelected())
self.assertFalse(item3.isSelected())
self.assertEqual(len(focused_item_spy), 1)

view.selectNextItemAbove()
self.assertFalse(item1.isSelected())
self.assertTrue(item2.isSelected())
self.assertFalse(item3.isSelected())
self.assertEqual(len(focused_item_spy), 2)

view.selectNextItemAbove()
self.assertFalse(item1.isSelected())
self.assertFalse(item2.isSelected())
self.assertTrue(item3.isSelected())
self.assertEqual(len(focused_item_spy), 3)

view.selectNextItemAbove() # already top most
self.assertFalse(item1.isSelected())
self.assertFalse(item2.isSelected())
self.assertTrue(item3.isSelected())
self.assertEqual(len(focused_item_spy), 3)

view.selectNextItemBelow()
self.assertFalse(item1.isSelected())
self.assertTrue(item2.isSelected())
self.assertFalse(item3.isSelected())
self.assertEqual(len(focused_item_spy), 4)

view.selectNextItemBelow()
self.assertTrue(item1.isSelected())
self.assertFalse(item2.isSelected())
self.assertFalse(item3.isSelected())
self.assertEqual(len(focused_item_spy), 5)

view.selectNextItemBelow() # back to bottom most
self.assertTrue(item1.isSelected())
self.assertFalse(item2.isSelected())
self.assertFalse(item3.isSelected())
self.assertEqual(len(focused_item_spy), 5)


if __name__ == '__main__':
unittest.main()

0 comments on commit 4cba2b9

Please sign in to comment.