diff --git a/python/gui/gui.sip b/python/gui/gui.sip index 24058e03b3e..bfa244b4eaa 100644 --- a/python/gui/gui.sip +++ b/python/gui/gui.sip @@ -63,6 +63,7 @@ %Include qgsdetaileditemwidget.sip %Include qgsdial.sip %Include qgsdialog.sip +%Include qgsdockwidget.sip %Include qgsencodingfiledialog.sip %Include qgserrordialog.sip %Include qgsexpressionbuilderdialog.sip diff --git a/python/gui/qgsdockwidget.sip b/python/gui/qgsdockwidget.sip new file mode 100644 index 00000000000..0f021a56d7f --- /dev/null +++ b/python/gui/qgsdockwidget.sip @@ -0,0 +1,61 @@ +/** \ingroup gui + * \class QgsDockWidget + * QDockWidget subclass with more fine-grained control over how the widget is closed or opened. + * \note added in 2.16 + */ + +class QgsDockWidget : QDockWidget +{ +%TypeHeaderCode +#include +%End + + public: + + /** Constructor for QgsDockWidget. + * @param parent parent widget + */ + explicit QgsDockWidget( QWidget* parent /TransferThis/ = nullptr ); + + public slots: + + /** Sets the dock widget as visible to a user, ie both shown and raised to the front. + * @param visible set to true to show the dock to the user, or false to hide the dock. + * When setting a dock as user visible, the dock will be opened (if it is not already + * opened) and raised to the front. + * When setting as hidden, the following logic is used: + * - hiding a dock which is open but not raised (ie hidden by another tab) will have no + * effect, and the dock will still be opened and hidden by the other tab + * - hiding a dock which is open and raised (ie, user visible) will cause the dock to + * be closed + * - hiding a dock which is closed has no effect and raises no signals + * @see isUserVisible() + */ + void setUserVisible( bool visible ); + + /** Returns true if the dock is both opened and raised to the front (ie not hidden by + * any other tabs. + * @see setUserVisible() + */ + bool isUserVisible() const; + + protected: + + virtual void closeEvent( QCloseEvent * ); + virtual void showEvent( QShowEvent* event ); + + signals: + + /** Emitted when dock widget is closed (or opened). + * @param wasClosed will be true if dock widget was closed, or false if dock widget was opened + * @see opened() + */ + void closed( bool wasClosed ); + + /** Emitted when dock widget is opened (or closed). + * @param wasOpened will be true if dock widget was opened, or false if dock widget was closed + * @see closed() + */ + void opened( bool wasOpened ); + +}; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index e2d0e5ab1ca..28d2c525b20 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -135,6 +135,7 @@ #include "qgsdatasourceuri.h" #include "qgsdatumtransformdialog.h" #include "qgsdoublespinbox.h" +#include "qgsdockwidget.h" #include "qgsdxfexport.h" #include "qgsdxfexportdialog.h" #include "qgsdecorationcopyright.h" @@ -769,13 +770,13 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh mUndoDock->hide(); startProfile( "Map Style dock" ); - mMapStylingDock = new QDockWidget( this ); + mMapStylingDock = new QgsDockWidget( this ); mMapStylingDock->setWindowTitle( tr( "Map Styling" ) ); mMapStylingDock->setObjectName( "MapStyling" ); mMapStyleWidget = new QgsMapStylingWidget( mMapCanvas, mMapStylePanelFactories ); mMapStylingDock->setWidget( mMapStyleWidget ); connect( mMapStyleWidget, SIGNAL( styleChanged( QgsMapLayer* ) ), this, SLOT( updateLabelToolButtons() ) ); -// connect( mMapStylingDock, SIGNAL( visibilityChanged( bool ) ), mActionStyleDock, SLOT( setChecked( bool ) ) ); + connect( mMapStylingDock, SIGNAL( visibilityChanged( bool ) ), mActionStyleDock, SLOT( setChecked( bool ) ) ); addDockWidget( Qt::RightDockWidgetArea, mMapStylingDock ); mMapStylingDock->hide(); @@ -5800,7 +5801,7 @@ void QgisApp::setMapStyleDockLayer( QgsMapLayer* layer ) void QgisApp::mapStyleDock( bool enabled ) { - mMapStylingDock->setVisible( enabled ); + mMapStylingDock->setUserVisible( enabled ); setMapStyleDockLayer( activeLayer() ); } diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index f9cc29b30ee..171214d34d1 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -49,6 +49,7 @@ class QgsStatusBarMagnifierWidget; class QgsStatusBarScaleWidget; class QgsContrastEnhancement; class QgsCustomLayerOrderWidget; +class QgsDockWidget; class QgsDoubleSpinBox; class QgsFeature; class QgsGeometry; @@ -1730,7 +1731,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QgsSnappingDialog *mSnappingDialog; QgsPluginManager *mPluginManager; - QDockWidget *mMapStylingDock; + QgsDockWidget *mMapStylingDock; QgsMapStylingWidget* mMapStyleWidget; QgsComposerManager *mComposerManager; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e09aae4691d..289d9cbca5e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -204,6 +204,7 @@ SET(QGIS_GUI_SRCS qgsdetaileditemwidget.cpp qgsdial.cpp qgsdialog.cpp + qgsdockwidget.cpp qgsencodingfiledialog.cpp qgserrordialog.cpp qgsexpressionbuilderdialog.cpp @@ -360,6 +361,7 @@ SET(QGIS_GUI_MOC_HDRS qgsdetaileditemwidget.h qgsdial.h qgsdialog.h + qgsdockwidget.h qgsencodingfiledialog.h qgserrordialog.h qgsexpressionbuilderdialog.h diff --git a/src/gui/qgsdockwidget.cpp b/src/gui/qgsdockwidget.cpp new file mode 100644 index 00000000000..464285989dc --- /dev/null +++ b/src/gui/qgsdockwidget.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + qgsdockwidget.cpp + ----------------- + begin : June 2016 + copyright : (C) 2016 by 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 "qgsdockwidget.h" + + +QgsDockWidget::QgsDockWidget( QWidget* parent ) + : QDockWidget( parent ) + , mVisibleAndActive( false ) +{ + connect( this, SIGNAL( visibilityChanged( bool ) ), this, SLOT( handleVisibilityChanged( bool ) ) ); +} + +void QgsDockWidget::setUserVisible( bool visible ) +{ + if ( visible ) + { + if ( mVisibleAndActive ) + return; + + show(); + raise(); + } + else + { + if ( !mVisibleAndActive ) + return; + + hide(); + } +} + +bool QgsDockWidget::isUserVisible() const +{ + return mVisibleAndActive; +} + +void QgsDockWidget::closeEvent( QCloseEvent* e ) +{ + emit closed( true ); + emit opened( false ); + QDockWidget::closeEvent( e ); +} + +void QgsDockWidget::showEvent( QShowEvent* e ) +{ + emit closed( false ); + emit opened( true ); + QDockWidget::showEvent( e ); +} + +void QgsDockWidget::handleVisibilityChanged( bool visible ) +{ + mVisibleAndActive = visible; +} + diff --git a/src/gui/qgsdockwidget.h b/src/gui/qgsdockwidget.h new file mode 100644 index 00000000000..cfd8d645d6d --- /dev/null +++ b/src/gui/qgsdockwidget.h @@ -0,0 +1,86 @@ +/*************************************************************************** + qgsdockwidget.h + --------------- + begin : June 2016 + copyright : (C) 2016 by 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 + +/** \ingroup gui + * \class QgsDockWidget + * QDockWidget subclass with more fine-grained control over how the widget is closed or opened. + * \note added in 2.16 + */ + +class GUI_EXPORT QgsDockWidget : public QDockWidget +{ + Q_OBJECT + + public: + + /** Constructor for QgsDockWidget. + * @param parent parent widget + */ + explicit QgsDockWidget( QWidget* parent = nullptr ); + + public slots: + + /** Sets the dock widget as visible to a user, ie both shown and raised to the front. + * @param visible set to true to show the dock to the user, or false to hide the dock. + * When setting a dock as user visible, the dock will be opened (if it is not already + * opened) and raised to the front. + * When setting as hidden, the following logic is used: + * - hiding a dock which is open but not raised (ie hidden by another tab) will have no + * effect, and the dock will still be opened and hidden by the other tab + * - hiding a dock which is open and raised (ie, user visible) will cause the dock to + * be closed + * - hiding a dock which is closed has no effect and raises no signals + * @see isUserVisible() + */ + void setUserVisible( bool visible ); + + /** Returns true if the dock is both opened and raised to the front (ie not hidden by + * any other tabs. + * @see setUserVisible() + */ + bool isUserVisible() const; + + protected: + + virtual void closeEvent( QCloseEvent * ) override; + virtual void showEvent( QShowEvent* event ) override; + + signals: + + /** Emitted when dock widget is closed (or opened). + * @param wasClosed will be true if dock widget was closed, or false if dock widget was opened + * @see opened() + */ + void closed( bool wasClosed ); + + /** Emitted when dock widget is opened (or closed). + * @param wasOpened will be true if dock widget was opened, or false if dock widget was closed + * @see closed() + */ + void opened( bool wasOpened ); + + private slots: + + void handleVisibilityChanged( bool visible ); + + private: + + bool mVisibleAndActive; + +}; diff --git a/tests/src/gui/CMakeLists.txt b/tests/src/gui/CMakeLists.txt index 1f15140fa62..32e3af9bf0b 100644 --- a/tests/src/gui/CMakeLists.txt +++ b/tests/src/gui/CMakeLists.txt @@ -129,6 +129,7 @@ ADD_QGIS_TEST(zoomtest testqgsmaptoolzoom.cpp) ADD_QGIS_TEST(doublespinbox testqgsdoublespinbox.cpp) ADD_QGIS_TEST(dualviewtest testqgsdualview.cpp) ADD_QGIS_TEST(attributeformtest testqgsattributeform.cpp) +ADD_QGIS_TEST(dockwidget testqgsdockwidget.cpp) ADD_QGIS_TEST(fieldexpressionwidget testqgsfieldexpressionwidget.cpp) ADD_QGIS_TEST(filewidget testqgsfilewidget.cpp) ADD_QGIS_TEST(focuswatcher testqgsfocuswatcher.cpp) diff --git a/tests/src/gui/testqgsdockwidget.cpp b/tests/src/gui/testqgsdockwidget.cpp new file mode 100644 index 00000000000..070073289c8 --- /dev/null +++ b/tests/src/gui/testqgsdockwidget.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** + testqgsdockwidget.cpp + ---------------------- + Date : June 2016 + Copyright : (C) 2016 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 + +#include "qgsdockwidget.h" +#include +#include + +class TestQgsDockWidget: public QObject +{ + Q_OBJECT + private slots: + void initTestCase(); // will be called before the first testfunction is executed. + void cleanupTestCase(); // will be called after the last testfunction was executed. + void init(); // will be called before each testfunction is executed. + void cleanup(); // will be called after every testfunction. + + void testSignals(); + void testUserVisible(); + void testSetUserVisible(); + + private: + +}; + +void TestQgsDockWidget::initTestCase() +{ +} + +void TestQgsDockWidget::cleanupTestCase() +{ +} + +void TestQgsDockWidget::init() +{ +} + +void TestQgsDockWidget::cleanup() +{ +} + +void TestQgsDockWidget::testSignals() +{ + QWidget* w = new QWidget(); + QApplication::setActiveWindow( w ); //required for focus events + QgsDockWidget* d = new QgsDockWidget( w ); + + QSignalSpy spyClosed( d, SIGNAL( closed( bool ) ) ); + QSignalSpy spyOpened( d, SIGNAL( opened( bool ) ) ); + + w->show(); + + d->show(); + QCOMPARE( spyClosed.count(), 1 ); + QCOMPARE( spyClosed.last().at( 0 ).toBool(), false ); + QCOMPARE( spyOpened.count(), 1 ); + QCOMPARE( spyOpened.last().at( 0 ).toBool(), true ); + + d->close(); + QCOMPARE( spyClosed.count(), 2 ); + QCOMPARE( spyClosed.last().at( 0 ).toBool(), true ); + QCOMPARE( spyOpened.count(), 2 ); + QCOMPARE( spyOpened.last().at( 0 ).toBool(), false ); + + delete w; +} + +void TestQgsDockWidget::testUserVisible() +{ + QgsDockWidget* w = new QgsDockWidget(); + QVERIFY( !w->isUserVisible() ); + + w->show(); + QVERIFY( w->isUserVisible() ); + + w->hide(); + QVERIFY( !w->isUserVisible() ); + delete w; +} + +void TestQgsDockWidget::testSetUserVisible() +{ + QMainWindow* w = new QMainWindow(); + QApplication::setActiveWindow( w ); //required for focus events + QgsDockWidget* d1 = new QgsDockWidget( w ); + QgsDockWidget* d2 = new QgsDockWidget( w ); + w->addDockWidget( Qt::RightDockWidgetArea, d1 ); + w->addDockWidget( Qt::RightDockWidgetArea, d2 ); + w->tabifyDockWidget( d1, d2 ); + w->show(); + + QVERIFY( d2->isUserVisible() ); + QVERIFY( !d1->isUserVisible() ); + + // showing dock widgets + + // already visible + d2->setUserVisible( true ); + QVERIFY( d2->isUserVisible() ); + QVERIFY( d2->isVisible() ); + QVERIFY( !d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + // visible, but hidden by other dock + d1->setUserVisible( true ); + QVERIFY( !d2->isUserVisible() ); + QVERIFY( d2->isVisible() ); + QVERIFY( d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + // hidden + d2->hide(); + d2->setUserVisible( true ); + QVERIFY( d2->isUserVisible() ); + QVERIFY( d2->isVisible() ); + QVERIFY( !d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + // hiding dock widgets + + // already hidden by other tab + d1->setUserVisible( false ); + QVERIFY( d2->isUserVisible() ); + QVERIFY( d2->isVisible() ); + QVERIFY( !d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + // already hidden + d2->hide(); + d1->raise(); //shouldn't be necessary outside of tests + QVERIFY( !d2->isUserVisible() ); + QVERIFY( !d2->isVisible() ); + QVERIFY( d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + d2->setUserVisible( false ); + QVERIFY( !d2->isUserVisible() ); + QVERIFY( !d2->isVisible() ); + QVERIFY( d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + // setting active dock as not user visible should hide it + d2->show(); + d1->raise(); + QVERIFY( !d2->isUserVisible() ); + QVERIFY( d2->isVisible() ); + QVERIFY( d1->isUserVisible() ); + QVERIFY( d1->isVisible() ); + + d1->setUserVisible( false ); + QVERIFY( d2->isVisible() ); + QVERIFY( !d1->isUserVisible() ); + QVERIFY( !d1->isVisible() ); + + delete w; + +} + +QTEST_MAIN( TestQgsDockWidget ) +#include "testqgsdockwidget.moc"