diff --git a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index ce026fdf3d9a..3a6de9ba3281 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -585,6 +585,13 @@ Returns the capacities .. versionadded:: 3.26 %End + QString formatCommonAngleSnapping( double angle ); +%Docstring +Returns the formatted label for common angle snapping option. + +.. versionadded:: 3.32 +%End + signals: void pushWarning( const QString &message ); @@ -1035,6 +1042,19 @@ Could be used by widgets to capture the focus when a field is being edited. .. versionadded:: 3.8 %End + void valueCommonAngleSnappingChanged( double angle ); +%Docstring +Emitted whenever the snapping to common angle option changes, angle = 0 means that the functionality is disabled. + +.. versionadded:: 3.32 +%End + + void commonAngleSnappingShowInFloaterChanged( bool enabled ); +%Docstring +Emitted whenever the option to show common angle snapping in the floater changes. + +.. versionadded:: 3.32 +%End private: //! event filter for line edits in the dock UI (angle/distance/x/y line edits) diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 908006eecca9..55ebfec287fb 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -37,7 +37,6 @@ #include - QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent ) : QgsDockWidget( parent ) , mMapCanvas( canvas ) @@ -117,37 +116,39 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * connect( mWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut ); // config menu - QMenu *menu = new QMenu( this ); + mCommonAngleActionsMenu = new QMenu( this ); + + QAction *showCommonAngleSnappingInFloaterAction = new QAction( tr( "Show setting in the floater" ), mCommonAngleActionsMenu ); + showCommonAngleSnappingInFloaterAction->setCheckable( true ); + + mCommonAngleActionsMenu->addAction( showCommonAngleSnappingInFloaterAction ); + // common angles - QActionGroup *angleButtonGroup = new QActionGroup( menu ); // actions are exclusive for common angles + QActionGroup *angleButtonGroup = new QActionGroup( mCommonAngleActionsMenu ); // actions are exclusive for common angles mCommonAngleActions = QMap(); QList< QPair< double, QString > > commonAngles; QString menuText; - const QList anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} ); + const QList anglesDouble( { 0.0, 0.1, 0.5, 1.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} ); for ( QList::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it ) { - if ( *it == 0 ) - menuText = tr( "Do Not Snap to Common Angles" ); - else - menuText = QString( tr( "%1, %2, %3, %4°…" ) ).arg( *it, 0, 'f', 1 ).arg( *it * 2, 0, 'f', 1 ).arg( *it * 3, 0, 'f', 1 ).arg( *it * 4, 0, 'f', 1 ); - commonAngles << QPair( *it, menuText ); + commonAngles << QPair( *it, formatCommonAngleSnapping( *it ) ); } for ( QList< QPair >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it ) { - QAction *action = new QAction( it->second, menu ); + QAction *action = new QAction( it->second, mCommonAngleActionsMenu ); action->setCheckable( true ); action->setChecked( it->first == mCommonAngleConstraint ); - menu->addAction( action ); + mCommonAngleActionsMenu->addAction( action ); angleButtonGroup->addAction( action ); mCommonAngleActions.insert( action, it->first ); } qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup ); - mSettingsAction->setMenu( menu ); + mSettingsAction->setMenu( mCommonAngleActionsMenu ); mSettingsAction->setCheckable( true ); - mSettingsAction->setToolTip( tr( "Snap to common angles" ) ); + mSettingsAction->setToolTip( "" + tr( "Snap to common angles" ) + "
(" + tr( "press n to cycle through the options" ) + ")" ); mSettingsAction->setChecked( mCommonAngleConstraint != 0 ); - connect( menu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered ); + connect( mCommonAngleActionsMenu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered ); // Construction modes QMenu *constructionMenu = new QMenu( this ); @@ -167,7 +168,7 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * constructionToolBar->setMenu( constructionMenu ); constructionToolBar->setObjectName( QStringLiteral( "ConstructionButton" ) ); - mConstructionAction->setMenu( menu ); + mConstructionAction->setMenu( mCommonAngleActionsMenu ); mConstructionAction->setCheckable( true ); mConstructionAction->setToolTip( tr( "Construction Tools" ) ); // connect( constructionMenu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered ); @@ -216,12 +217,29 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * connect( mToggleFloaterAction, &QAction::triggered, mFloater, &QgsAdvancedDigitizingFloater::setActive ); mToggleFloaterAction->setChecked( mFloater->active() ); + mShowCommonAngleInFloater = QgsSettings().value( QStringLiteral( "/Cad/CommonAngleShowInFloater" ), false ).toBool(); + connect( showCommonAngleSnappingInFloaterAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::commonAngleSnappingShowInFloaterChanged ); + showCommonAngleSnappingInFloaterAction->setChecked( mShowCommonAngleInFloater ); + connect( showCommonAngleSnappingInFloaterAction, &QAction::triggered, this, [ = ]( bool checked ) + { + mShowCommonAngleInFloater = checked; + QgsSettings().setValue( QStringLiteral( "/Cad/CommonAngleShowInFloater" ), checked ); + } ); + updateCapacity( true ); connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, this, [ = ] { updateCapacity( true ); } ); disable(); } +QString QgsAdvancedDigitizingDockWidget::formatCommonAngleSnapping( double angle ) +{ + if ( angle == 0 ) + return tr( "Do Not Snap to Common Angles" ); + else + return QString( tr( "%1, %2, %3, %4°…" ) ).arg( angle, 0, 'f', 1 ).arg( angle * 2, 0, 'f', 1 ).arg( angle * 3, 0, 'f', 1 ).arg( angle * 4, 0, 'f', 1 ); +} + void QgsAdvancedDigitizingDockWidget::setX( const QString &value, WidgetSetMode mode ) { mXLineEdit->setText( value ); @@ -349,6 +367,12 @@ void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled ) switchZM(); emit cadEnabledChanged( enabled ); + if ( enabled ) + { + emit commonAngleSnappingShowInFloaterChanged( mShowCommonAngleInFloater ); + emit valueCommonAngleSnappingChanged( mCommonAngleConstraint ); + } + mLastSnapMatch = QgsPointLocator::Match(); } @@ -521,6 +545,7 @@ void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action ) mCommonAngleConstraint = ica.value(); QgsSettings().setValue( QStringLiteral( "/Cad/CommonAngle" ), ica.value() ); mSettingsAction->setChecked( mCommonAngleConstraint != 0 ); + emit valueCommonAngleSnappingChanged( mCommonAngleConstraint ); return; } } @@ -1611,6 +1636,27 @@ bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e ) } break; } + case Qt::Key_N: + { + if ( type == QEvent::ShortcutOverride ) + { + const QList constActionValues { mCommonAngleActions.values() }; + const int currentAngleActionIndex { constActionValues.indexOf( mCommonAngleConstraint ) }; + const QList constActions { mCommonAngleActions.keys( ) }; + QAction *nextAngleAction; + if ( e->modifiers() == Qt::ShiftModifier ) + { + nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 ); + } + else + { + nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 ); + } + nextAngleAction->trigger(); + e->accept(); + } + break; + } default: { return false; // continues diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index 13152093dfdb..24498cfd6a57 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -545,6 +545,12 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private */ CadCapacities capacities() const { return mCapacities; }; + /** + * Returns the formatted label for common angle snapping option. + * \since QGIS 3.32 + */ + QString formatCommonAngleSnapping( double angle ); + signals: /** @@ -866,6 +872,17 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private */ void focusOnDistanceRequested(); + /** + * Emitted whenever the snapping to common angle option changes, angle = 0 means that the functionality is disabled. + * \since QGIS 3.32 + */ + void valueCommonAngleSnappingChanged( double angle ); + + /** + * Emitted whenever the option to show common angle snapping in the floater changes. + * \since QGIS 3.32 + */ + void commonAngleSnappingShowInFloaterChanged( bool enabled ); private slots: //! Sets the between line constraint by clicking on the perpendicular/parallel buttons @@ -1033,6 +1050,9 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private //! Convenient method to convert a 2D Point to a QgsPoint QgsPoint pointXYToPoint( const QgsPointXY &point ) const; + QMenu *mCommonAngleActionsMenu = nullptr; + bool mShowCommonAngleInFloater = false; + friend class TestQgsAdvancedDigitizing; friend class TestQgsAdvancedDigitizingDockWidget; }; diff --git a/src/gui/qgsadvanceddigitizingfloater.cpp b/src/gui/qgsadvanceddigitizingfloater.cpp index c9c07d693dcb..f458fc586e48 100644 --- a/src/gui/qgsadvanceddigitizingfloater.cpp +++ b/src/gui/qgsadvanceddigitizingfloater.cpp @@ -37,6 +37,8 @@ QgsAdvancedDigitizingFloater::QgsAdvancedDigitizingFloater( QgsMapCanvas *canvas hideIfDisabled(); + enabledCommonAngleSnapping( cadDockWidget->commonAngleConstraint() ); + // This is required to be able to track mouse move events mMapCanvas->viewport()->installEventFilter( this ); mMapCanvas->viewport()->setMouseTracking( true ); @@ -57,6 +59,8 @@ QgsAdvancedDigitizingFloater::QgsAdvancedDigitizingFloater( QgsMapCanvas *canvas connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::valueZChanged, this, &QgsAdvancedDigitizingFloater::changeZ ); connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::valueMChanged, this, &QgsAdvancedDigitizingFloater::changeM ); connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::valueAngleChanged, this, &QgsAdvancedDigitizingFloater::changeAngle ); + connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::valueCommonAngleSnappingChanged, this, &QgsAdvancedDigitizingFloater::changeCommonAngleSnapping ); + connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::commonAngleSnappingShowInFloaterChanged, this, &QgsAdvancedDigitizingFloater::enabledCommonAngleSnapping ); connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::valueDistanceChanged, this, &QgsAdvancedDigitizingFloater::changeDistance ); connect( cadDockWidget, &QgsAdvancedDigitizingDockWidget::lockXChanged, this, &QgsAdvancedDigitizingFloater::changeLockX ); @@ -114,6 +118,7 @@ QgsAdvancedDigitizingFloater::QgsAdvancedDigitizingFloater( QgsMapCanvas *canvas connect( angleWatcher, &QgsFocusWatcher::focusOut, cadDockWidget, [ = ]() { cadDockWidget->setAngle( mAngleLineEdit->text(), QgsAdvancedDigitizingDockWidget::WidgetSetMode::FocusOut ); } ); QgsFocusWatcher *distanceWatcher = new QgsFocusWatcher( mDistanceLineEdit ); connect( distanceWatcher, &QgsFocusWatcher::focusOut, cadDockWidget, [ = ]() { cadDockWidget->setDistance( mDistanceLineEdit->text(), QgsAdvancedDigitizingDockWidget::WidgetSetMode::FocusOut ); } ); + changeCommonAngleSnapping( mCadDockWidget->commonAngleConstraint() ); } @@ -191,6 +196,11 @@ void QgsAdvancedDigitizingFloater::changeM( const QString &text ) mMLineEdit->setText( text ); } +void QgsAdvancedDigitizingFloater::changeCommonAngleSnapping( double angle ) +{ + mCommonAngleSnappingLineEdit->setText( qgsDoubleNear( angle, 0.0 ) ? tr( "disabled" ) : QLocale().toString( angle ).append( QStringLiteral( "°" ) ) ); +} + void QgsAdvancedDigitizingFloater::changeDistance( const QString &text ) { mDistanceLineEdit->setText( text ); @@ -443,6 +453,13 @@ void QgsAdvancedDigitizingFloater::enabledChangedDistance( bool enabled ) adjustSize(); } +void QgsAdvancedDigitizingFloater::enabledCommonAngleSnapping( bool enabled ) +{ + mCommonAngleSnappingLineEdit->setVisible( enabled ); + mCommonAngleSnappingLabel->setVisible( enabled ); + adjustSize(); +} + void QgsAdvancedDigitizingFloater::enabledChangedAngle( bool enabled ) { mAngleLineEdit->setVisible( enabled ); diff --git a/src/gui/qgsadvanceddigitizingfloater.h b/src/gui/qgsadvanceddigitizingfloater.h index 7f0261ac0ec7..229706ab1fa7 100644 --- a/src/gui/qgsadvanceddigitizingfloater.h +++ b/src/gui/qgsadvanceddigitizingfloater.h @@ -75,6 +75,7 @@ class GUI_EXPORT QgsAdvancedDigitizingFloater : public QWidget, private Ui::QgsA void changeY( const QString &text ); void changeZ( const QString &text ); void changeM( const QString &text ); + void changeCommonAngleSnapping( double angle ); void changeDistance( const QString &text ); void changeAngle( const QString &text ); void changeLockX( bool locked ); @@ -101,6 +102,7 @@ class GUI_EXPORT QgsAdvancedDigitizingFloater : public QWidget, private Ui::QgsA void enabledChangedM( bool enabled ); void enabledChangedAngle( bool enabled ); void enabledChangedDistance( bool enabled ); + void enabledCommonAngleSnapping( bool enabled ); private: diff --git a/src/ui/qgsadvanceddigitizingfloaterbase.ui b/src/ui/qgsadvanceddigitizingfloaterbase.ui index f6633f2206c6..c21b436cee66 100644 --- a/src/ui/qgsadvanceddigitizingfloaterbase.ui +++ b/src/ui/qgsadvanceddigitizingfloaterbase.ui @@ -7,7 +7,7 @@ 0 0 310 - 189 + 204 @@ -72,28 +72,50 @@ 0 - - + + + + + 40 + 0 + + - z + - + + + false + + + + + + + y Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - x + a Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + 40 + 0 + + @@ -103,9 +125,6 @@ false - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - @@ -118,8 +137,8 @@ - - + + 40 @@ -137,27 +156,30 @@ - - + + + + + - y + - + + + false - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - + + + 40 0 - - - - @@ -166,10 +188,20 @@ - - + + - a + m + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + z Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -195,40 +227,25 @@ - - + + - m + x Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 40 - 0 - - + + - - - - - false + - - - - - 40 - 0 - - + + - @@ -245,8 +262,6 @@ mXLineEdit mYLineEdit - - - +