Fix style dock button not synced to dock state (fix #14862)

Add a new class QgsDockWidget which has finer control over setting
and retrieving the dock visibility, to account for dock widgets
which are open but hidden by other docks
This commit is contained in:
Nyall Dawson 2016-06-09 10:59:47 +10:00
parent a033e81add
commit cb4dacfebc
9 changed files with 402 additions and 4 deletions

View File

@ -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

View File

@ -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 <qgsdockwidget.h>
%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 );
};

View File

@ -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() );
}

View File

@ -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;

View File

@ -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

71
src/gui/qgsdockwidget.cpp Normal file
View File

@ -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;
}

86
src/gui/qgsdockwidget.h Normal file
View File

@ -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 <QDockWidget>
/** \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;
};

View File

@ -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)

View File

@ -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 <QtTest/QtTest>
#include "qgsdockwidget.h"
#include <QApplication>
#include <QMainWindow>
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"