From 27077c875e5d4f7b979a544b0815d3c127ecd021 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 18 May 2017 10:35:33 +1000 Subject: [PATCH] Add a QgsStatusBar widget/interface for adding messages/widgets to main window statusbar QStatusBar gives almost no control over display and placement of child widgets. It's not possible to subclass and reimplement either, due to how QMainWindow works internally, and also due to the special handling for the size grip and other platform specific handling in QStatusBar. Instead, we embed a single QgsStatusBar covering the whole real status bar. All child widgets and temporary messages instead are pushed to the QgsStatusBar instead - giving us as much control as we desire over how these widgets are placed and their behavior. As a result the locator widget has been moved to its logical placement on the left of the status bar. All plugins must ensure that they use the status bar interface available via iface.statusBarIface() instead of directly interacting with the status bar (e.g. iface.mainWindow().statusBar()...) --- doc/api_break.dox | 2 + python/gui/gui.sip | 1 + python/gui/qgisinterface.sip | 2 + python/gui/qgsstatusbar.sip | 98 ++++++++++++++++++ src/app/gps/qgsgpsinformationwidget.cpp | 3 +- src/app/nodetool/qgsnodetool.cpp | 6 +- src/app/nodetool/qgsselectedfeature.cpp | 7 +- src/app/qgisapp.cpp | 63 ++++++------ src/app/qgisapp.h | 5 + src/app/qgisappinterface.cpp | 5 + src/app/qgisappinterface.h | 2 + src/app/qgscustomization.cpp | 7 +- src/app/qgsmaptoolcircularstringradius.cpp | 3 +- src/app/qgsmaptoolfeatureaction.cpp | 3 +- src/app/qgsmaptoolidentifyaction.cpp | 3 +- src/gui/CMakeLists.txt | 2 + src/gui/locator/qgslocatorwidget.cpp | 4 +- src/gui/qgisinterface.h | 9 ++ src/gui/qgsstatusbar.cpp | 82 +++++++++++++++ src/gui/qgsstatusbar.h | 113 +++++++++++++++++++++ 20 files changed, 376 insertions(+), 44 deletions(-) create mode 100644 python/gui/qgsstatusbar.sip create mode 100644 src/gui/qgsstatusbar.cpp create mode 100644 src/gui/qgsstatusbar.h diff --git a/doc/api_break.dox b/doc/api_break.dox index 2cc55bcbdd1..c98f0ae2b50 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -401,6 +401,8 @@ are similar, but only apply to composer windows when they are exist. To access a from a project, the new QgsProject.instance().layoutManager() class should be used instead. Additionally, the new interface methods work with QgsComposerInterface objects instead of QgsComposerView objects. +- interaction with the main window status bar should no longer use the native Qt statusBar() method. +Instead iface.statusBarIface() should be used. CharacterWidget {#qgis_api_break_3_0_CharacterWidget} ------------------- diff --git a/python/gui/gui.sip b/python/gui/gui.sip index e77d648b573..70ce167522e 100644 --- a/python/gui/gui.sip +++ b/python/gui/gui.sip @@ -159,6 +159,7 @@ %Include qgsshortcutsmanager.sip %Include qgsslider.sip %Include qgssourceselectdialog.sip +%Include qgsstatusbar.sip %Include qgssublayersdialog.sip %Include qgssubstitutionlistwidget.sip %Include qgstablewidgetbase.sip diff --git a/python/gui/qgisinterface.sip b/python/gui/qgisinterface.sip index 176f0499bad..3ff23cd2798 100644 --- a/python/gui/qgisinterface.sip +++ b/python/gui/qgisinterface.sip @@ -509,6 +509,8 @@ class QgisInterface : QObject /** Get timeout for timed messages: default of 5 seconds */ virtual int messageTimeout() = 0; + virtual QgsStatusBar *statusBarIface() = 0; + /** * Registers a locator \a filter for the app's locator bar. Ownership of the filter is transferred to the * locator. diff --git a/python/gui/qgsstatusbar.sip b/python/gui/qgsstatusbar.sip new file mode 100644 index 00000000000..abc09242dfe --- /dev/null +++ b/python/gui/qgsstatusbar.sip @@ -0,0 +1,98 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsstatusbar.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsStatusBar : QWidget +{ +%Docstring + A proxy widget for QStatusBar. + + Unlike QStatusBar, QgsStatusBar allows finer control of widget placement, including + the option to locate permanent widgets on the left side of the bar. + + QgsStatusBar is designed to be embedded into an existing + window's QStatusBar, as a permanent widget. This allows reuse of the special QStatusBar handling + for resize grips and other platform specific status bar tweaks. + + Instead of adding child widgets and showing messages directly in the window's status bar, + these widgets (and messages) should instead be added into the embedded QgsStatusBar. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsstatusbar.h" +%End + public: + + enum Anchor + { + AnchorLeft, + AnchorRight, + }; + + QgsStatusBar( QWidget *parent /TransferThis/ = 0 ); +%Docstring + Constructor for QgsStatusBar. +%End + + void addPermanentWidget( QWidget *widget /Transfer/, int stretch = 0, Anchor anchor = AnchorRight ); +%Docstring + Adds the given ``widget`` permanently to this status bar, reparenting the widget if it isn't already a child + of this object. + + The ``stretch`` parameter is used to compute a suitable size for the given widget as the status bar + grows and shrinks. The default stretch factor is 0, i.e giving the widget a minimum of space. + + The ``anchor`` parameter controls which side of the status bar the widget should be anchored to. +%End + + void removeWidget( QWidget *widget ); +%Docstring + Removes a ``widget`` from the status bar. Ownership of the widget remains unchanged, and the + widget itself is not deleted. +%End + + QString currentMessage() const; +%Docstring + Returns the current message shown in the status bar. +.. seealso:: showMessage() + :rtype: str +%End + + public slots: + + void showMessage( const QString &message, int timeout = 0 ); +%Docstring + Displays the given ``message`` for the specified number of milli-seconds (``timeout``). + If ``timeout`` is 0 (default), the message remains displayed until the clearMessage() + slot is called or until the showMessage() slot is called again to change the message. +.. seealso:: clearMessage() +.. seealso:: currentMessage() +%End + + void clearMessage(); +%Docstring + Removes any temporary message being shown. +.. seealso:: showMessage() +%End + +}; + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsstatusbar.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/app/gps/qgsgpsinformationwidget.cpp b/src/app/gps/qgsgpsinformationwidget.cpp index 5bf4b93f155..f201fbb0814 100644 --- a/src/app/gps/qgsgpsinformationwidget.cpp +++ b/src/app/gps/qgsgpsinformationwidget.cpp @@ -36,6 +36,7 @@ #include "qgsvectorlayer.h" #include "qgswkbptr.h" #include "qgssettings.h" +#include "qgsstatusbar.h" // QWT Charting widget @@ -1122,5 +1123,5 @@ void QgsGPSInformationWidget::setStatusIndicator( const FixStatus statusValue ) void QgsGPSInformationWidget::showStatusBarMessage( const QString &msg ) { - QgisApp::instance()->statusBar()->showMessage( msg ); + QgisApp::instance()->statusBarIface()->showMessage( msg ); } diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index a36e0e71c2e..767cf675918 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -31,7 +31,7 @@ #include "qgssnappingutils.h" #include "qgsvectorlayer.h" #include "qgsvertexmarker.h" - +#include "qgsstatusbar.h" #include "qgisapp.h" #include "qgsselectedfeature.h" #include "qgsnodeeditor.h" @@ -1797,7 +1797,7 @@ void QgsNodeTool::validationFinished() GeometryValidation &validation = *it; if ( validation.validator == validator ) { - QStatusBar *sb = QgisApp::instance()->statusBar(); + QgsStatusBar *sb = QgisApp::instance()->statusBarIface(); sb->showMessage( tr( "Validation finished (%n error(s) found).", "number of geometry errors", validation.errorMarkers.size() ) ); if ( validation.errorMarkers.isEmpty() ) { @@ -1838,7 +1838,7 @@ void QgsNodeTool::GeometryValidation::addError( QgsGeometry::Error e ) errorMarkers << marker; } - QStatusBar *sb = QgisApp::instance()->statusBar(); + QgsStatusBar *sb = QgisApp::instance()->statusBarIface(); sb->showMessage( e.what() ); sb->setToolTip( errors ); } diff --git a/src/app/nodetool/qgsselectedfeature.cpp b/src/app/nodetool/qgsselectedfeature.cpp index eaadda81d50..5362c32b9cb 100644 --- a/src/app/nodetool/qgsselectedfeature.cpp +++ b/src/app/nodetool/qgsselectedfeature.cpp @@ -27,6 +27,7 @@ #include "qgisapp.h" #include "qgslayertreeview.h" #include "qgsproject.h" +#include "qgsstatusbar.h" QgsSelectedFeature::QgsSelectedFeature( QgsFeatureId featureId, QgsVectorLayer *vlayer, @@ -189,7 +190,7 @@ void QgsSelectedFeature::validateGeometry( QgsGeometry *g ) connect( mValidator, &QThread::finished, this, &QgsSelectedFeature::validationFinished ); mValidator->start(); - QStatusBar *sb = QgisApp::instance()->statusBar(); + QgsStatusBar *sb = QgisApp::instance()->statusBarIface(); sb->showMessage( tr( "Validation started." ) ); } @@ -212,14 +213,14 @@ void QgsSelectedFeature::addError( QgsGeometry::Error e ) mGeomErrorMarkers << marker; } - QStatusBar *sb = QgisApp::instance()->statusBar(); + QgsStatusBar *sb = QgisApp::instance()->statusBarIface(); sb->showMessage( e.what() ); sb->setToolTip( mTip ); } void QgsSelectedFeature::validationFinished() { - QStatusBar *sb = QgisApp::instance()->statusBar(); + QgsStatusBar *sb = QgisApp::instance()->statusBarIface(); sb->showMessage( tr( "Validation finished (%n error(s) found).", "number of geometry errors", mGeomErrorMarkers.size() ) ); } diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 042437b9b5a..4f3461c597d 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -248,6 +248,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgssourceselectdialog.h" #include "qgssponsors.h" #include "qgsstatisticalsummarydockwidget.h" +#include "qgsstatusbar.h" #include "qgsstatusbarcoordinateswidget.h" #include "qgsstatusbarmagnifierwidget.h" #include "qgsstatusbarscalewidget.h" @@ -2521,21 +2522,25 @@ void QgisApp::createStatusBar() //remove borders from children under Windows statusBar()->setStyleSheet( QStringLiteral( "QStatusBar::item {border: none;}" ) ); + mStatusBar = new QgsStatusBar(); + + statusBar()->addPermanentWidget( mStatusBar, 10 ); + // Add a panel to the status bar for the scale, coords and progress // And also rendering suppression checkbox - mProgressBar = new QProgressBar( statusBar() ); + mProgressBar = new QProgressBar( mStatusBar ); mProgressBar->setObjectName( QStringLiteral( "mProgressBar" ) ); mProgressBar->setMaximumWidth( 100 ); mProgressBar->hide(); mProgressBar->setWhatsThis( tr( "Progress bar that displays the status " "of rendering layers and other time-intensive operations" ) ); - statusBar()->addPermanentWidget( mProgressBar, 1 ); + mStatusBar->addPermanentWidget( mProgressBar, 1 ); connect( mMapCanvas, &QgsMapCanvas::renderStarting, this, &QgisApp::canvasRefreshStarted ); connect( mMapCanvas, &QgsMapCanvas::mapCanvasRefreshed, this, &QgisApp::canvasRefreshFinished ); - mTaskManagerWidget = new QgsTaskManagerStatusBarWidget( QgsApplication::taskManager(), statusBar() ); - statusBar()->addPermanentWidget( mTaskManagerWidget, 0 ); + mTaskManagerWidget = new QgsTaskManagerStatusBarWidget( QgsApplication::taskManager(), mStatusBar ); + mStatusBar->addPermanentWidget( mTaskManagerWidget, 0 ); // Bumped the font up one point size since 8 was too // small on some platforms. A point size of 9 still provides @@ -2544,29 +2549,29 @@ void QgisApp::createStatusBar() statusBar()->setFont( myFont ); //coords status bar widget - mCoordsEdit = new QgsStatusBarCoordinatesWidget( statusBar() ); + mCoordsEdit = new QgsStatusBarCoordinatesWidget( mStatusBar ); mCoordsEdit->setObjectName( QStringLiteral( "mCoordsEdit" ) ); mCoordsEdit->setMapCanvas( mMapCanvas ); mCoordsEdit->setFont( myFont ); - statusBar()->addPermanentWidget( mCoordsEdit, 0 ); + mStatusBar->addPermanentWidget( mCoordsEdit, 0 ); - mScaleWidget = new QgsStatusBarScaleWidget( mMapCanvas, statusBar() ); + mScaleWidget = new QgsStatusBarScaleWidget( mMapCanvas, mStatusBar ); mScaleWidget->setObjectName( QStringLiteral( "mScaleWidget" ) ); mScaleWidget->setFont( myFont ); connect( mScaleWidget, &QgsStatusBarScaleWidget::scaleLockChanged, mMapCanvas, &QgsMapCanvas::setScaleLocked ); - statusBar()->addPermanentWidget( mScaleWidget, 0 ); + mStatusBar->addPermanentWidget( mScaleWidget, 0 ); // zoom widget - mMagnifierWidget = new QgsStatusBarMagnifierWidget( statusBar() ); + mMagnifierWidget = new QgsStatusBarMagnifierWidget( mStatusBar ); mMagnifierWidget->setObjectName( QStringLiteral( "mMagnifierWidget" ) ); mMagnifierWidget->setFont( myFont ); connect( mMapCanvas, &QgsMapCanvas::magnificationChanged, mMagnifierWidget, &QgsStatusBarMagnifierWidget::updateMagnification ); connect( mMagnifierWidget, &QgsStatusBarMagnifierWidget::magnificationChanged, mMapCanvas, &QgsMapCanvas::setMagnificationFactor ); mMagnifierWidget->updateMagnification( QSettings().value( QStringLiteral( "/qgis/magnifier_factor_default" ), 1.0 ).toDouble() ); - statusBar()->addPermanentWidget( mMagnifierWidget, 0 ); + mStatusBar->addPermanentWidget( mMagnifierWidget, 0 ); // add a widget to show/set current rotation - mRotationLabel = new QLabel( QString(), statusBar() ); + mRotationLabel = new QLabel( QString(), mStatusBar ); mRotationLabel->setObjectName( QStringLiteral( "mRotationLabel" ) ); mRotationLabel->setFont( myFont ); mRotationLabel->setMinimumWidth( 10 ); @@ -2576,9 +2581,9 @@ void QgisApp::createStatusBar() mRotationLabel->setFrameStyle( QFrame::NoFrame ); mRotationLabel->setText( tr( "Rotation" ) ); mRotationLabel->setToolTip( tr( "Current clockwise map rotation in degrees" ) ); - statusBar()->addPermanentWidget( mRotationLabel, 0 ); + mStatusBar->addPermanentWidget( mRotationLabel, 0 ); - mRotationEdit = new QgsDoubleSpinBox( statusBar() ); + mRotationEdit = new QgsDoubleSpinBox( mStatusBar ); mRotationEdit->setObjectName( QStringLiteral( "mRotationEdit" ) ); mRotationEdit->setClearValue( 0.0 ); mRotationEdit->setKeyboardTracking( false ); @@ -2593,13 +2598,13 @@ void QgisApp::createStatusBar() "in degrees. It also allows editing to set " "the rotation" ) ); mRotationEdit->setToolTip( tr( "Current clockwise map rotation in degrees" ) ); - statusBar()->addPermanentWidget( mRotationEdit, 0 ); + mStatusBar->addPermanentWidget( mRotationEdit, 0 ); connect( mRotationEdit, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgisApp::userRotation ); showRotation(); // render suppression status bar widget - mRenderSuppressionCBox = new QCheckBox( tr( "Render" ), statusBar() ); + mRenderSuppressionCBox = new QCheckBox( tr( "Render" ), mStatusBar ); mRenderSuppressionCBox->setObjectName( QStringLiteral( "mRenderSuppressionCBox" ) ); mRenderSuppressionCBox->setChecked( true ); mRenderSuppressionCBox->setFont( myFont ); @@ -2608,11 +2613,11 @@ void QgisApp::createStatusBar() "events. When not checked, no rendering is done. This allows you " "to add a large number of layers and symbolize them before rendering." ) ); mRenderSuppressionCBox->setToolTip( tr( "Toggle map rendering" ) ); - statusBar()->addPermanentWidget( mRenderSuppressionCBox, 0 ); + mStatusBar->addPermanentWidget( mRenderSuppressionCBox, 0 ); // On the fly projection status bar icon // Changed this to a tool button since a QPushButton is // sculpted on OS X and the icon is never displayed [gsherman] - mOnTheFlyProjectionStatusButton = new QToolButton( statusBar() ); + mOnTheFlyProjectionStatusButton = new QToolButton( mStatusBar ); mOnTheFlyProjectionStatusButton->setAutoRaise( true ); mOnTheFlyProjectionStatusButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); mOnTheFlyProjectionStatusButton->setObjectName( QStringLiteral( "mOntheFlyProjectionStatusButton" ) ); @@ -2628,10 +2633,10 @@ void QgisApp::createStatusBar() "to open coordinate reference system dialog" ) ); connect( mOnTheFlyProjectionStatusButton, &QAbstractButton::clicked, this, &QgisApp::projectPropertiesProjections );//bring up the project props dialog when clicked - statusBar()->addPermanentWidget( mOnTheFlyProjectionStatusButton, 0 ); - statusBar()->showMessage( tr( "Ready" ) ); + mStatusBar->addPermanentWidget( mOnTheFlyProjectionStatusButton, 0 ); + mStatusBar->showMessage( tr( "Ready" ) ); - mMessageButton = new QToolButton( statusBar() ); + mMessageButton = new QToolButton( mStatusBar ); mMessageButton->setAutoRaise( true ); mMessageButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mMessageLogRead.svg" ) ) ); mMessageButton->setToolTip( tr( "Messages" ) ); @@ -2639,10 +2644,10 @@ void QgisApp::createStatusBar() mMessageButton->setObjectName( QStringLiteral( "mMessageLogViewerButton" ) ); mMessageButton->setMaximumHeight( mScaleWidget->height() ); mMessageButton->setCheckable( true ); - statusBar()->addPermanentWidget( mMessageButton, 0 ); + mStatusBar->addPermanentWidget( mMessageButton, 0 ); - mLocatorWidget = new QgsLocatorWidget( statusBar() ); - statusBar()->addPermanentWidget( mLocatorWidget ); + mLocatorWidget = new QgsLocatorWidget( mStatusBar ); + mStatusBar->addPermanentWidget( mLocatorWidget, 0, QgsStatusBar::AnchorLeft ); QShortcut *locatorShortCut = new QShortcut( QKeySequence( tr( "Ctrl+K" ) ), this ); connect( locatorShortCut, &QShortcut::activated, mLocatorWidget, [ = ] { mLocatorWidget->search( QString() ); } ); locatorShortCut->setObjectName( QStringLiteral( "Locator" ) ); @@ -5222,7 +5227,7 @@ void QgisApp::enableProjectMacros() bool QgisApp::addProject( const QString &projectFile ) { QFileInfo pfi( projectFile ); - statusBar()->showMessage( tr( "Loading project: %1" ).arg( pfi.fileName() ) ); + mStatusBar->showMessage( tr( "Loading project: %1" ).arg( pfi.fileName() ) ); qApp->processEvents(); QApplication::setOverrideCursor( Qt::WaitCursor ); @@ -5249,7 +5254,7 @@ bool QgisApp::addProject( const QString &projectFile ) buttons |= QMessageBox::Ok; } QApplication::restoreOverrideCursor(); - statusBar()->clearMessage(); + mStatusBar->clearMessage(); int r = QMessageBox::critical( this, tr( "Unable to open project" ), @@ -5355,7 +5360,7 @@ bool QgisApp::addProject( const QString &projectFile ) mMapCanvas->freeze( false ); mMapCanvas->refresh(); - statusBar()->showMessage( tr( "Project loaded" ), 3000 ); + mStatusBar->showMessage( tr( "Project loaded" ), 3000 ); return true; } // QgisApp::addProject(QString projectFile) @@ -5421,7 +5426,7 @@ bool QgisApp::fileSave() if ( QgsProject::instance()->write() ) { setTitleBarText_( *this ); // update title bar - statusBar()->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 ); + mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 ); saveRecentProjectPath( fullPath.filePath() ); @@ -5473,7 +5478,7 @@ void QgisApp::fileSaveAs() if ( QgsProject::instance()->write() ) { setTitleBarText_( *this ); // update title bar - statusBar()->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 ); + mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 ); // add this to the list of recently used project files saveRecentProjectPath( fullPath.filePath() ); mProjectLastModified = fullPath.lastModified(); @@ -10885,7 +10890,7 @@ void QgisApp::updateMouseCoordinatePrecision() void QgisApp::showStatusMessage( const QString &message ) { - statusBar()->showMessage( message ); + mStatusBar->showMessage( message ); } void QgisApp::displayMapToolMessage( const QString &message, QgsMessageBar::MessageLevel level ) diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index ff8335b6de2..8300cf5b20b 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -92,6 +92,7 @@ class QgsVectorLayer; class QgsVectorLayerTools; class QgsWelcomePage; class QgsOptionsWidgetFactory; +class QgsStatusBar; class QDomDocument; class QNetworkReply; @@ -557,6 +558,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QToolBar *databaseToolBar() { return mDatabaseToolBar; } QToolBar *webToolBar() { return mWebToolBar; } + QgsStatusBar *statusBarIface() { return mStatusBar; } + //! return CAD dock widget QgsAdvancedDigitizingDockWidget *cadDockWidget() { return mAdvancedDigitizingDockWidget; } @@ -1979,6 +1982,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QgsLocatorWidget *mLocatorWidget = nullptr; + QgsStatusBar *mStatusBar = nullptr; + friend class TestQgisAppPython; }; diff --git a/src/app/qgisappinterface.cpp b/src/app/qgisappinterface.cpp index 5d3bcdca429..a0018354052 100644 --- a/src/app/qgisappinterface.cpp +++ b/src/app/qgisappinterface.cpp @@ -736,6 +736,11 @@ int QgisAppInterface::messageTimeout() return qgis->messageTimeout(); } +QgsStatusBar *QgisAppInterface::statusBarIface() +{ + return qgis->statusBarIface(); +} + void QgisAppInterface::registerLocatorFilter( QgsLocatorFilter *filter ) { qgis->mLocatorWidget->locator()->registerFilter( filter ); diff --git a/src/app/qgisappinterface.h b/src/app/qgisappinterface.h index 28f6114a98e..56991a187f4 100644 --- a/src/app/qgisappinterface.h +++ b/src/app/qgisappinterface.h @@ -494,6 +494,8 @@ class APP_EXPORT QgisAppInterface : public QgisInterface //! Get timeout for timed messages: default of 5 seconds virtual int messageTimeout() override; + QgsStatusBar *statusBarIface() override; + void registerLocatorFilter( QgsLocatorFilter *filter ) override; void deregisterLocatorFilter( QgsLocatorFilter *filter ) override; diff --git a/src/app/qgscustomization.cpp b/src/app/qgscustomization.cpp index 14c99542e60..d8c77a441ae 100644 --- a/src/app/qgscustomization.cpp +++ b/src/app/qgscustomization.cpp @@ -18,6 +18,7 @@ #include "qgisapp.h" #include "qgsapplication.h" #include "qgslogger.h" +#include "qgsstatusbar.h" #include #include @@ -599,7 +600,7 @@ void QgsCustomization::createTreeItemStatus() topItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable ); topItem->setCheckState( 0, Qt::Checked ); - QStatusBar *sb = QgisApp::instance()->statusBar(); + QgsStatusBar *sb = QgisApp::instance()->statusBarIface(); Q_FOREACH ( QObject *obj, sb->children() ) { if ( obj->inherits( "QWidget" ) && !obj->objectName().isEmpty() ) @@ -653,7 +654,7 @@ void QgsCustomization::updateMainWindow( QMenu *toolBarMenu ) if ( !mEnabled ) return; - QMainWindow *mw = QgisApp::instance(); + QgisApp *mw = QgisApp::instance(); QMenuBar *menubar = mw->menuBar(); mSettings->beginGroup( QStringLiteral( "Customization/Menus" ) ); @@ -738,7 +739,7 @@ void QgsCustomization::updateMainWindow( QMenu *toolBarMenu ) { mSettings->beginGroup( QStringLiteral( "Customization/StatusBar" ) ); - QStatusBar *sb = mw->statusBar(); + QgsStatusBar *sb = mw->statusBarIface(); Q_FOREACH ( QObject *obj, sb->children() ) { if ( obj->inherits( "QWidget" ) ) diff --git a/src/app/qgsmaptoolcircularstringradius.cpp b/src/app/qgsmaptoolcircularstringradius.cpp index 98bfff9b70a..022af7bc564 100644 --- a/src/app/qgsmaptoolcircularstringradius.cpp +++ b/src/app/qgsmaptoolcircularstringradius.cpp @@ -21,6 +21,7 @@ #include "qgsgeometryrubberband.h" #include "qgsmapcanvas.h" #include "qgspointv2.h" +#include "qgsstatusbar.h" #include #include #include @@ -167,7 +168,7 @@ void QgsMapToolCircularStringRadius::deleteRadiusSpinBox() { if ( mRadiusSpinBox ) { - QgisApp::instance()->statusBar()->removeWidget( mRadiusSpinBox ); + QgisApp::instance()->statusBarIface()->removeWidget( mRadiusSpinBox ); delete mRadiusSpinBox; mRadiusSpinBox = nullptr; } diff --git a/src/app/qgsmaptoolfeatureaction.cpp b/src/app/qgsmaptoolfeatureaction.cpp index 56a2d3af551..c57ca08e648 100644 --- a/src/app/qgsmaptoolfeatureaction.cpp +++ b/src/app/qgsmaptoolfeatureaction.cpp @@ -32,6 +32,7 @@ #include "qgsmaplayeractionregistry.h" #include "qgisapp.h" #include "qgsgui.h" +#include "qgsstatusbar.h" #include #include @@ -80,7 +81,7 @@ void QgsMapToolFeatureAction::canvasReleaseEvent( QgsMapMouseEvent *e ) } if ( !doAction( vlayer, e->x(), e->y() ) ) - QgisApp::instance()->statusBar()->showMessage( tr( "No features at this position found." ) ); + QgisApp::instance()->statusBarIface()->showMessage( tr( "No features at this position found." ) ); } void QgsMapToolFeatureAction::activate() diff --git a/src/app/qgsmaptoolidentifyaction.cpp b/src/app/qgsmaptoolidentifyaction.cpp index 8499edd0eb9..26855c51626 100644 --- a/src/app/qgsmaptoolidentifyaction.cpp +++ b/src/app/qgsmaptoolidentifyaction.cpp @@ -36,6 +36,7 @@ #include "qgsproject.h" #include "qgsrenderer.h" #include "qgsunittypes.h" +#include "qgsstatusbar.h" #include "qgssettings.h" #include @@ -135,7 +136,7 @@ void QgsMapToolIdentifyAction::canvasReleaseEvent( QgsMapMouseEvent *e ) if ( results.isEmpty() ) { resultsDialog()->clear(); - QgisApp::instance()->statusBar()->showMessage( tr( "No features at this position found." ) ); + QgisApp::instance()->statusBarIface()->showMessage( tr( "No features at this position found." ) ); } else { diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 2b70dce84c9..16e2038055f 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -306,6 +306,7 @@ SET(QGIS_GUI_SRCS qgssublayersdialog.cpp qgssubstitutionlistwidget.cpp qgssqlcomposerdialog.cpp + qgsstatusbar.cpp qgstablewidgetbase.cpp qgstabwidget.cpp qgstablewidgetitem.cpp @@ -451,6 +452,7 @@ SET(QGIS_GUI_MOC_HDRS qgsshortcutsmanager.h qgsslider.h qgssqlcomposerdialog.h + qgsstatusbar.h qgssublayersdialog.h qgssubstitutionlistwidget.h qgstablewidgetbase.h diff --git a/src/gui/locator/qgslocatorwidget.cpp b/src/gui/locator/qgslocatorwidget.cpp index dd3aa7b4494..e271468a2cb 100644 --- a/src/gui/locator/qgslocatorwidget.cpp +++ b/src/gui/locator/qgslocatorwidget.cpp @@ -54,8 +54,8 @@ QgsLocatorWidget::QgsLocatorWidget( QWidget *parent ) // setup floating container widget mResultsContainer = new QgsFloatingWidget( parent ? parent->window() : nullptr ); mResultsContainer->setAnchorWidget( mLineEdit ); - mResultsContainer->setAnchorPoint( QgsFloatingWidget::BottomRight ); - mResultsContainer->setAnchorWidgetPoint( QgsFloatingWidget::TopRight ); + mResultsContainer->setAnchorPoint( QgsFloatingWidget::BottomLeft ); + mResultsContainer->setAnchorWidgetPoint( QgsFloatingWidget::TopLeft ); QHBoxLayout *containerLayout = new QHBoxLayout( this ); containerLayout->setMargin( 0 ); diff --git a/src/gui/qgisinterface.h b/src/gui/qgisinterface.h index 9f1e5533cc9..d46ded18008 100644 --- a/src/gui/qgisinterface.h +++ b/src/gui/qgisinterface.h @@ -43,6 +43,7 @@ class QgsVectorLayer; class QgsVectorLayerTools; class QgsOptionsWidgetFactory; class QgsLocatorFilter; +class QgsStatusBar; #include #include @@ -652,6 +653,14 @@ class GUI_EXPORT QgisInterface : public QObject //! Get timeout for timed messages: default of 5 seconds virtual int messageTimeout() = 0; + /** + * Returns a pointer to the app's status bar interface. This should be + * used for interacting and adding widgets and messages to the app's + * status bar (do not use the native Qt statusBar() method). + * \since QGIS 3.0 + */ + virtual QgsStatusBar *statusBarIface() = 0; + /** * Registers a locator \a filter for the app's locator bar. Ownership of the filter is transferred to the * locator. diff --git a/src/gui/qgsstatusbar.cpp b/src/gui/qgsstatusbar.cpp new file mode 100644 index 00000000000..79fc8c35b26 --- /dev/null +++ b/src/gui/qgsstatusbar.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + qgsstatusbar.cpp + ---------------- + begin : May 2017 + copyright : (C) 2017 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 "qgsstatusbar.h" +#include +#include +#include + +QgsStatusBar::QgsStatusBar( QWidget *parent ) + : QWidget( parent ) +{ + mLayout = new QHBoxLayout(); + mLayout->setMargin( 0 ); + mLayout->setContentsMargins( 2, 0, 2, 0 ); + mLayout->setSpacing( 6 ); + + mLabel = new QLabel( QString() ); + mLayout->addWidget( mLabel, 1 ); + setLayout( mLayout ); +} + +void QgsStatusBar::addPermanentWidget( QWidget *widget, int stretch, Anchor anchor ) +{ + switch ( anchor ) + { + case AnchorLeft: + mLayout->insertWidget( 0, widget, stretch, Qt::AlignLeft ); + break; + + case AnchorRight: + mLayout->addWidget( widget, stretch, Qt::AlignLeft ); + break; + } +} + +void QgsStatusBar::removeWidget( QWidget *widget ) +{ + mLayout->removeWidget( widget ); +} + +QString QgsStatusBar::currentMessage() const +{ + return mLabel->text(); +} + +void QgsStatusBar::showMessage( const QString &text, int timeout ) +{ + mLabel->setText( text ); + if ( timeout > 0 ) + { + if ( !mTempMessageTimer ) + { + mTempMessageTimer = new QTimer( this ); + connect( mTempMessageTimer, &QTimer::timeout, this, &QgsStatusBar::clearMessage ); + } + mTempMessageTimer->start( timeout ); + } + else if ( mTempMessageTimer ) + { + delete mTempMessageTimer; + mTempMessageTimer = nullptr; + } +} + +void QgsStatusBar::clearMessage() +{ + mLabel->setText( QString() ); +} diff --git a/src/gui/qgsstatusbar.h b/src/gui/qgsstatusbar.h new file mode 100644 index 00000000000..d9a148ebdff --- /dev/null +++ b/src/gui/qgsstatusbar.h @@ -0,0 +1,113 @@ +/*************************************************************************** + qgsstatusbar.h + -------------- + begin : May 2017 + copyright : (C) 2017 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. * + * * + ***************************************************************************/ + +#ifndef QGSSTATUSBAR_H +#define QGSSTATUSBAR_H + +#include "qgis_gui.h" +#include "qgis_sip.h" +#include + +class QHBoxLayout; +class QLabel; + +/** + * \class QgsStatusBar + * \ingroup gui + * A proxy widget for QStatusBar. + * + * Unlike QStatusBar, QgsStatusBar allows finer control of widget placement, including + * the option to locate permanent widgets on the left side of the bar. + * + * QgsStatusBar is designed to be embedded into an existing + * window's QStatusBar, as a permanent widget. This allows reuse of the special QStatusBar handling + * for resize grips and other platform specific status bar tweaks. + * + * Instead of adding child widgets and showing messages directly in the window's status bar, + * these widgets (and messages) should instead be added into the embedded QgsStatusBar. + * + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsStatusBar : public QWidget +{ + Q_OBJECT + + public: + + //! Placement anchor for widgets + enum Anchor + { + AnchorLeft = 0, //!< Anchor widget to left of status bar + AnchorRight, //!< Anchor widget to right of status bar + }; + + /** + * Constructor for QgsStatusBar. + */ + QgsStatusBar( QWidget *parent SIP_TRANSFERTHIS = nullptr ); + + /** + * Adds the given \a widget permanently to this status bar, reparenting the widget if it isn't already a child + * of this object. + * + * The \a stretch parameter is used to compute a suitable size for the given widget as the status bar + * grows and shrinks. The default stretch factor is 0, i.e giving the widget a minimum of space. + * + * The \a anchor parameter controls which side of the status bar the widget should be anchored to. + */ + void addPermanentWidget( QWidget *widget SIP_TRANSFER, int stretch = 0, Anchor anchor = AnchorRight ); + + /** + * Removes a \a widget from the status bar. Ownership of the widget remains unchanged, and the + * widget itself is not deleted. + */ + void removeWidget( QWidget *widget ); + + /** + * Returns the current message shown in the status bar. + * \see showMessage() + */ + QString currentMessage() const; + + public slots: + + /** + * Displays the given \a message for the specified number of milli-seconds (\a timeout). + * If \a timeout is 0 (default), the message remains displayed until the clearMessage() + * slot is called or until the showMessage() slot is called again to change the message. + * \see clearMessage() + * \see currentMessage() + */ + void showMessage( const QString &message, int timeout = 0 ); + + /** + * Removes any temporary message being shown. + * \see showMessage() + */ + void clearMessage(); + + private: + + QHBoxLayout *mLayout = nullptr; + QLabel *mLabel = nullptr; + QTimer *mTempMessageTimer = nullptr; + +}; + +#endif // QGSSTATUSBAR_H + +