Port select next above/below actions

This commit is contained in:
Nyall Dawson 2017-10-03 15:06:47 +10:00
parent dbb31253e9
commit 4cba2b90ef
7 changed files with 234 additions and 0 deletions

View File

@ -177,6 +177,8 @@ class QgsLayoutView: QGraphicsView
Selects all items in the view. Selects all items in the view.
.. seealso:: deselectAll() .. seealso:: deselectAll()
.. seealso:: invertSelection() .. seealso:: invertSelection()
.. seealso:: selectNextItemAbove()
.. seealso:: selectNextItemBelow()
%End %End
void deselectAll(); void deselectAll();
@ -192,6 +194,22 @@ class QgsLayoutView: QGraphicsView
and deselecting and selected items. and deselecting and selected items.
.. seealso:: selectAll() .. seealso:: selectAll()
.. seealso:: deselectAll() .. 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 %End
void viewChanged(); void viewChanged();

View File

@ -194,9 +194,14 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionSelectAll, &QAction::triggered, mView, &QgsLayoutView::selectAll ); connect( mActionSelectAll, &QAction::triggered, mView, &QgsLayoutView::selectAll );
connect( mActionDeselectAll, &QAction::triggered, mView, &QgsLayoutView::deselectAll ); connect( mActionDeselectAll, &QAction::triggered, mView, &QgsLayoutView::deselectAll );
connect( mActionInvertSelection, &QAction::triggered, mView, &QgsLayoutView::invertSelection ); 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( mActionAddPages, &QAction::triggered, this, &QgsLayoutDesignerDialog::addPages );
connect( mActionUnlockAll, &QAction::triggered, this, &QgsLayoutDesignerDialog::unlockAllItems );
connect( mActionLockItems, &QAction::triggered, this, &QgsLayoutDesignerDialog::lockSelectedItems );
//create status bar labels //create status bar labels
mStatusCursorXLabel = new QLabel( mStatusBar ); mStatusCursorXLabel = new QLabel( mStatusBar );
mStatusCursorXLabel->setMinimumWidth( 100 ); mStatusCursorXLabel->setMinimumWidth( 100 );
@ -477,6 +482,22 @@ void QgsLayoutDesignerDialog::snapToItems( bool enabled )
mLayout->snapper().setSnapToItems( enabled ); mLayout->snapper().setSnapToItems( enabled );
} }
void QgsLayoutDesignerDialog::unlockAllItems()
{
if ( mLayout )
{
mLayout->unlockAllItems();
}
}
void QgsLayoutDesignerDialog::lockSelectedItems()
{
if ( mLayout )
{
mLayout->lockSelectedItems();
}
}
void QgsLayoutDesignerDialog::closeEvent( QCloseEvent * ) void QgsLayoutDesignerDialog::closeEvent( QCloseEvent * )
{ {
emit aboutToClose(); emit aboutToClose();

View File

@ -149,6 +149,18 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
*/ */
void snapToItems( bool enabled ); 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: signals:
/** /**

View File

@ -24,6 +24,7 @@
#include "qgslayoutviewtooltemporarymousepan.h" #include "qgslayoutviewtooltemporarymousepan.h"
#include "qgslayoutmousehandles.h" #include "qgslayoutmousehandles.h"
#include "qgslayoutruler.h" #include "qgslayoutruler.h"
#include "qgslayoutmodel.h"
#include "qgssettings.h" #include "qgssettings.h"
#include "qgsrectangle.h" #include "qgsrectangle.h"
#include "qgsapplication.h" #include "qgsapplication.h"
@ -353,6 +354,50 @@ void QgsLayoutView::invertSelection()
emit itemFocused( focusedItem ); 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 ) void QgsLayoutView::mousePressEvent( QMouseEvent *event )
{ {
mSnapMarker->setVisible( false ); mSnapMarker->setVisible( false );

View File

@ -219,6 +219,8 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
* Selects all items in the view. * Selects all items in the view.
* \see deselectAll() * \see deselectAll()
* \see invertSelection() * \see invertSelection()
* \see selectNextItemAbove()
* \see selectNextItemBelow()
*/ */
void selectAll(); void selectAll();
@ -237,6 +239,22 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
*/ */
void invertSelection(); 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. * Updates associated rulers and other widgets after view extent or zoom has changed.
* This should be called after calling any of the QGraphicsView * This should be called after calling any of the QGraphicsView

View File

@ -151,9 +151,17 @@
<addaction name="mActionSelectNextBelow"/> <addaction name="mActionSelectNextBelow"/>
<addaction name="mActionSelectNextAbove"/> <addaction name="mActionSelectNextAbove"/>
</widget> </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="mLayoutMenu"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
<addaction name="mMenuView"/> <addaction name="mMenuView"/>
<addaction name="menuLayout"/>
<addaction name="mItemMenu"/> <addaction name="mItemMenu"/>
</widget> </widget>
<widget class="QToolBar" name="mNavigationToolbar"> <widget class="QToolBar" name="mNavigationToolbar">
@ -171,6 +179,19 @@
<addaction name="mActionZoomActual"/> <addaction name="mActionZoomActual"/>
<addaction name="mActionZoomAll"/> <addaction name="mActionZoomAll"/>
</widget> </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"> <action name="mActionClose">
<property name="text"> <property name="text">
<string>&amp;Close</string> <string>&amp;Close</string>
@ -536,6 +557,33 @@
<string>Ctrl+Alt+]</string> <string>Ctrl+Alt+]</string>
</property> </property>
</action> </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> </widget>
<resources> <resources>
<include location="../../../images/images.qrc"/> <include location="../../../images/images.qrc"/>

View File

@ -179,6 +179,78 @@ class TestQgsLayoutView(unittest.TestCase):
self.assertFalse(item2.isSelected()) self.assertFalse(item2.isSelected())
self.assertFalse(item3.isSelected()) # locked 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__': if __name__ == '__main__':
unittest.main() unittest.main()