Skip to content

Commit cb4dacf

Browse files
committedJun 9, 2016
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
1 parent a033e81 commit cb4dacf

File tree

9 files changed

+402
-4
lines changed

9 files changed

+402
-4
lines changed
 

‎python/gui/gui.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
%Include qgsdetaileditemwidget.sip
6464
%Include qgsdial.sip
6565
%Include qgsdialog.sip
66+
%Include qgsdockwidget.sip
6667
%Include qgsencodingfiledialog.sip
6768
%Include qgserrordialog.sip
6869
%Include qgsexpressionbuilderdialog.sip

‎python/gui/qgsdockwidget.sip

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/** \ingroup gui
2+
* \class QgsDockWidget
3+
* QDockWidget subclass with more fine-grained control over how the widget is closed or opened.
4+
* \note added in 2.16
5+
*/
6+
7+
class QgsDockWidget : QDockWidget
8+
{
9+
%TypeHeaderCode
10+
#include <qgsdockwidget.h>
11+
%End
12+
13+
public:
14+
15+
/** Constructor for QgsDockWidget.
16+
* @param parent parent widget
17+
*/
18+
explicit QgsDockWidget( QWidget* parent /TransferThis/ = nullptr );
19+
20+
public slots:
21+
22+
/** Sets the dock widget as visible to a user, ie both shown and raised to the front.
23+
* @param visible set to true to show the dock to the user, or false to hide the dock.
24+
* When setting a dock as user visible, the dock will be opened (if it is not already
25+
* opened) and raised to the front.
26+
* When setting as hidden, the following logic is used:
27+
* - hiding a dock which is open but not raised (ie hidden by another tab) will have no
28+
* effect, and the dock will still be opened and hidden by the other tab
29+
* - hiding a dock which is open and raised (ie, user visible) will cause the dock to
30+
* be closed
31+
* - hiding a dock which is closed has no effect and raises no signals
32+
* @see isUserVisible()
33+
*/
34+
void setUserVisible( bool visible );
35+
36+
/** Returns true if the dock is both opened and raised to the front (ie not hidden by
37+
* any other tabs.
38+
* @see setUserVisible()
39+
*/
40+
bool isUserVisible() const;
41+
42+
protected:
43+
44+
virtual void closeEvent( QCloseEvent * );
45+
virtual void showEvent( QShowEvent* event );
46+
47+
signals:
48+
49+
/** Emitted when dock widget is closed (or opened).
50+
* @param wasClosed will be true if dock widget was closed, or false if dock widget was opened
51+
* @see opened()
52+
*/
53+
void closed( bool wasClosed );
54+
55+
/** Emitted when dock widget is opened (or closed).
56+
* @param wasOpened will be true if dock widget was opened, or false if dock widget was closed
57+
* @see closed()
58+
*/
59+
void opened( bool wasOpened );
60+
61+
};

‎src/app/qgisapp.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
#include "qgsdatasourceuri.h"
136136
#include "qgsdatumtransformdialog.h"
137137
#include "qgsdoublespinbox.h"
138+
#include "qgsdockwidget.h"
138139
#include "qgsdxfexport.h"
139140
#include "qgsdxfexportdialog.h"
140141
#include "qgsdecorationcopyright.h"
@@ -769,13 +770,13 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
769770
mUndoDock->hide();
770771

771772
startProfile( "Map Style dock" );
772-
mMapStylingDock = new QDockWidget( this );
773+
mMapStylingDock = new QgsDockWidget( this );
773774
mMapStylingDock->setWindowTitle( tr( "Map Styling" ) );
774775
mMapStylingDock->setObjectName( "MapStyling" );
775776
mMapStyleWidget = new QgsMapStylingWidget( mMapCanvas, mMapStylePanelFactories );
776777
mMapStylingDock->setWidget( mMapStyleWidget );
777778
connect( mMapStyleWidget, SIGNAL( styleChanged( QgsMapLayer* ) ), this, SLOT( updateLabelToolButtons() ) );
778-
// connect( mMapStylingDock, SIGNAL( visibilityChanged( bool ) ), mActionStyleDock, SLOT( setChecked( bool ) ) );
779+
connect( mMapStylingDock, SIGNAL( visibilityChanged( bool ) ), mActionStyleDock, SLOT( setChecked( bool ) ) );
779780

780781
addDockWidget( Qt::RightDockWidgetArea, mMapStylingDock );
781782
mMapStylingDock->hide();
@@ -5800,7 +5801,7 @@ void QgisApp::setMapStyleDockLayer( QgsMapLayer* layer )
58005801

58015802
void QgisApp::mapStyleDock( bool enabled )
58025803
{
5803-
mMapStylingDock->setVisible( enabled );
5804+
mMapStylingDock->setUserVisible( enabled );
58045805
setMapStyleDockLayer( activeLayer() );
58055806
}
58065807

‎src/app/qgisapp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class QgsStatusBarMagnifierWidget;
4949
class QgsStatusBarScaleWidget;
5050
class QgsContrastEnhancement;
5151
class QgsCustomLayerOrderWidget;
52+
class QgsDockWidget;
5253
class QgsDoubleSpinBox;
5354
class QgsFeature;
5455
class QgsGeometry;
@@ -1730,7 +1731,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
17301731
QgsSnappingDialog *mSnappingDialog;
17311732

17321733
QgsPluginManager *mPluginManager;
1733-
QDockWidget *mMapStylingDock;
1734+
QgsDockWidget *mMapStylingDock;
17341735
QgsMapStylingWidget* mMapStyleWidget;
17351736

17361737
QgsComposerManager *mComposerManager;

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ SET(QGIS_GUI_SRCS
204204
qgsdetaileditemwidget.cpp
205205
qgsdial.cpp
206206
qgsdialog.cpp
207+
qgsdockwidget.cpp
207208
qgsencodingfiledialog.cpp
208209
qgserrordialog.cpp
209210
qgsexpressionbuilderdialog.cpp
@@ -360,6 +361,7 @@ SET(QGIS_GUI_MOC_HDRS
360361
qgsdetaileditemwidget.h
361362
qgsdial.h
362363
qgsdialog.h
364+
qgsdockwidget.h
363365
qgsencodingfiledialog.h
364366
qgserrordialog.h
365367
qgsexpressionbuilderdialog.h

‎src/gui/qgsdockwidget.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/***************************************************************************
2+
qgsdockwidget.cpp
3+
-----------------
4+
begin : June 2016
5+
copyright : (C) 2016 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
19+
#include "qgsdockwidget.h"
20+
21+
22+
QgsDockWidget::QgsDockWidget( QWidget* parent )
23+
: QDockWidget( parent )
24+
, mVisibleAndActive( false )
25+
{
26+
connect( this, SIGNAL( visibilityChanged( bool ) ), this, SLOT( handleVisibilityChanged( bool ) ) );
27+
}
28+
29+
void QgsDockWidget::setUserVisible( bool visible )
30+
{
31+
if ( visible )
32+
{
33+
if ( mVisibleAndActive )
34+
return;
35+
36+
show();
37+
raise();
38+
}
39+
else
40+
{
41+
if ( !mVisibleAndActive )
42+
return;
43+
44+
hide();
45+
}
46+
}
47+
48+
bool QgsDockWidget::isUserVisible() const
49+
{
50+
return mVisibleAndActive;
51+
}
52+
53+
void QgsDockWidget::closeEvent( QCloseEvent* e )
54+
{
55+
emit closed( true );
56+
emit opened( false );
Code has comments. Press enter to view.
57+
QDockWidget::closeEvent( e );
58+
}
59+
60+
void QgsDockWidget::showEvent( QShowEvent* e )
61+
{
62+
emit closed( false );
63+
emit opened( true );
64+
QDockWidget::showEvent( e );
65+
}
66+
67+
void QgsDockWidget::handleVisibilityChanged( bool visible )
68+
{
69+
mVisibleAndActive = visible;
70+
}
71+

‎src/gui/qgsdockwidget.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/***************************************************************************
2+
qgsdockwidget.h
3+
---------------
4+
begin : June 2016
5+
copyright : (C) 2016 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include <QDockWidget>
19+
20+
/** \ingroup gui
21+
* \class QgsDockWidget
22+
* QDockWidget subclass with more fine-grained control over how the widget is closed or opened.
23+
* \note added in 2.16
24+
*/
25+
26+
class GUI_EXPORT QgsDockWidget : public QDockWidget
27+
{
28+
Q_OBJECT
29+
30+
public:
31+
32+
/** Constructor for QgsDockWidget.
33+
* @param parent parent widget
34+
*/
35+
explicit QgsDockWidget( QWidget* parent = nullptr );
36+
37+
public slots:
38+
39+
/** Sets the dock widget as visible to a user, ie both shown and raised to the front.
40+
* @param visible set to true to show the dock to the user, or false to hide the dock.
41+
* When setting a dock as user visible, the dock will be opened (if it is not already
42+
* opened) and raised to the front.
43+
* When setting as hidden, the following logic is used:
44+
* - hiding a dock which is open but not raised (ie hidden by another tab) will have no
45+
* effect, and the dock will still be opened and hidden by the other tab
46+
* - hiding a dock which is open and raised (ie, user visible) will cause the dock to
47+
* be closed
48+
* - hiding a dock which is closed has no effect and raises no signals
49+
* @see isUserVisible()
50+
*/
51+
void setUserVisible( bool visible );
52+
53+
/** Returns true if the dock is both opened and raised to the front (ie not hidden by
54+
* any other tabs.
55+
* @see setUserVisible()
56+
*/
57+
bool isUserVisible() const;
58+
59+
protected:
60+
61+
virtual void closeEvent( QCloseEvent * ) override;
62+
virtual void showEvent( QShowEvent* event ) override;
63+
64+
signals:
65+
66+
/** Emitted when dock widget is closed (or opened).
67+
* @param wasClosed will be true if dock widget was closed, or false if dock widget was opened
68+
* @see opened()
69+
*/
70+
void closed( bool wasClosed );
71+
72+
/** Emitted when dock widget is opened (or closed).
73+
* @param wasOpened will be true if dock widget was opened, or false if dock widget was closed
74+
* @see closed()
75+
*/
76+
void opened( bool wasOpened );
77+
78+
private slots:
79+
80+
void handleVisibilityChanged( bool visible );
81+
82+
private:
83+
84+
bool mVisibleAndActive;
85+
86+
};

‎tests/src/gui/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ ADD_QGIS_TEST(zoomtest testqgsmaptoolzoom.cpp)
129129
ADD_QGIS_TEST(doublespinbox testqgsdoublespinbox.cpp)
130130
ADD_QGIS_TEST(dualviewtest testqgsdualview.cpp)
131131
ADD_QGIS_TEST(attributeformtest testqgsattributeform.cpp)
132+
ADD_QGIS_TEST(dockwidget testqgsdockwidget.cpp)
132133
ADD_QGIS_TEST(fieldexpressionwidget testqgsfieldexpressionwidget.cpp)
133134
ADD_QGIS_TEST(filewidget testqgsfilewidget.cpp)
134135
ADD_QGIS_TEST(focuswatcher testqgsfocuswatcher.cpp)

‎tests/src/gui/testqgsdockwidget.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/***************************************************************************
2+
testqgsdockwidget.cpp
3+
----------------------
4+
Date : June 2016
5+
Copyright : (C) 2016 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
17+
#include <QtTest/QtTest>
18+
19+
#include "qgsdockwidget.h"
20+
#include <QApplication>
21+
#include <QMainWindow>
22+
23+
class TestQgsDockWidget: public QObject
24+
{
25+
Q_OBJECT
26+
private slots:
27+
void initTestCase(); // will be called before the first testfunction is executed.
28+
void cleanupTestCase(); // will be called after the last testfunction was executed.
29+
void init(); // will be called before each testfunction is executed.
30+
void cleanup(); // will be called after every testfunction.
31+
32+
void testSignals();
33+
void testUserVisible();
34+
void testSetUserVisible();
35+
36+
private:
37+
38+
};
39+
40+
void TestQgsDockWidget::initTestCase()
41+
{
42+
}
43+
44+
void TestQgsDockWidget::cleanupTestCase()
45+
{
46+
}
47+
48+
void TestQgsDockWidget::init()
49+
{
50+
}
51+
52+
void TestQgsDockWidget::cleanup()
53+
{
54+
}
55+
56+
void TestQgsDockWidget::testSignals()
57+
{
58+
QWidget* w = new QWidget();
59+
QApplication::setActiveWindow( w ); //required for focus events
60+
QgsDockWidget* d = new QgsDockWidget( w );
61+
62+
QSignalSpy spyClosed( d, SIGNAL( closed( bool ) ) );
63+
QSignalSpy spyOpened( d, SIGNAL( opened( bool ) ) );
64+
65+
w->show();
66+
67+
d->show();
68+
QCOMPARE( spyClosed.count(), 1 );
69+
QCOMPARE( spyClosed.last().at( 0 ).toBool(), false );
70+
QCOMPARE( spyOpened.count(), 1 );
71+
QCOMPARE( spyOpened.last().at( 0 ).toBool(), true );
72+
73+
d->close();
74+
QCOMPARE( spyClosed.count(), 2 );
75+
QCOMPARE( spyClosed.last().at( 0 ).toBool(), true );
76+
QCOMPARE( spyOpened.count(), 2 );
77+
QCOMPARE( spyOpened.last().at( 0 ).toBool(), false );
78+
79+
delete w;
80+
}
81+
82+
void TestQgsDockWidget::testUserVisible()
83+
{
84+
QgsDockWidget* w = new QgsDockWidget();
85+
QVERIFY( !w->isUserVisible() );
86+
87+
w->show();
88+
QVERIFY( w->isUserVisible() );
89+
90+
w->hide();
91+
QVERIFY( !w->isUserVisible() );
92+
delete w;
93+
}
94+
95+
void TestQgsDockWidget::testSetUserVisible()
96+
{
97+
QMainWindow* w = new QMainWindow();
98+
QApplication::setActiveWindow( w ); //required for focus events
99+
QgsDockWidget* d1 = new QgsDockWidget( w );
100+
QgsDockWidget* d2 = new QgsDockWidget( w );
101+
w->addDockWidget( Qt::RightDockWidgetArea, d1 );
102+
w->addDockWidget( Qt::RightDockWidgetArea, d2 );
103+
w->tabifyDockWidget( d1, d2 );
104+
w->show();
105+
106+
QVERIFY( d2->isUserVisible() );
107+
QVERIFY( !d1->isUserVisible() );
108+
109+
// showing dock widgets
110+
111+
// already visible
112+
d2->setUserVisible( true );
113+
QVERIFY( d2->isUserVisible() );
114+
QVERIFY( d2->isVisible() );
115+
QVERIFY( !d1->isUserVisible() );
116+
QVERIFY( d1->isVisible() );
117+
118+
// visible, but hidden by other dock
119+
d1->setUserVisible( true );
120+
QVERIFY( !d2->isUserVisible() );
121+
QVERIFY( d2->isVisible() );
122+
QVERIFY( d1->isUserVisible() );
123+
QVERIFY( d1->isVisible() );
124+
125+
// hidden
126+
d2->hide();
127+
d2->setUserVisible( true );
128+
QVERIFY( d2->isUserVisible() );
129+
QVERIFY( d2->isVisible() );
130+
QVERIFY( !d1->isUserVisible() );
131+
QVERIFY( d1->isVisible() );
132+
133+
// hiding dock widgets
134+
135+
// already hidden by other tab
136+
d1->setUserVisible( false );
137+
QVERIFY( d2->isUserVisible() );
138+
QVERIFY( d2->isVisible() );
139+
QVERIFY( !d1->isUserVisible() );
140+
QVERIFY( d1->isVisible() );
141+
142+
// already hidden
143+
d2->hide();
144+
d1->raise(); //shouldn't be necessary outside of tests
145+
QVERIFY( !d2->isUserVisible() );
146+
QVERIFY( !d2->isVisible() );
147+
QVERIFY( d1->isUserVisible() );
148+
QVERIFY( d1->isVisible() );
149+
150+
d2->setUserVisible( false );
151+
QVERIFY( !d2->isUserVisible() );
152+
QVERIFY( !d2->isVisible() );
153+
QVERIFY( d1->isUserVisible() );
154+
QVERIFY( d1->isVisible() );
155+
156+
// setting active dock as not user visible should hide it
157+
d2->show();
158+
d1->raise();
159+
QVERIFY( !d2->isUserVisible() );
160+
QVERIFY( d2->isVisible() );
161+
QVERIFY( d1->isUserVisible() );
162+
QVERIFY( d1->isVisible() );
163+
164+
d1->setUserVisible( false );
165+
QVERIFY( d2->isVisible() );
166+
QVERIFY( !d1->isUserVisible() );
167+
QVERIFY( !d1->isVisible() );
168+
169+
delete w;
170+
171+
}
172+
173+
QTEST_MAIN( TestQgsDockWidget )
174+
#include "testqgsdockwidget.moc"

2 commit comments

Comments
 (2)

nirvn commented on Jun 9, 2016

@nirvn
Contributor

Nice.

It'd be good to seize of the opportunity to refactor the layer panel's toolbar buttons (I'm mostly thinking of the "filter legend by map content" and "filter legend by expression" buttons). Merging those two would make space for the style dock button to be moved there instead of floating to the right.

NathanW2 commented on Jun 9, 2016

@NathanW2
Member

Very nice. That is a good fix.

Please sign in to comment.