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