Compare commits

...

20 Commits

Author SHA1 Message Date
Till Frankenbach
5b6f9208c4
Merge 4c7ae43e466d89e8962d24241eaec484e90e62ca into b927df884feb840b67724af5c82c8088a9d20bfe 2025-07-02 23:03:36 +02:00
Loïc Bartoletti
b927df884f
Merge pull request #62484 from jef-n/fix-62478
fix #62478 (followup eac401c009)
2025-07-02 06:09:23 +02:00
qgis-bot
fe299930cd auto sipify 🍺 2025-07-01 23:40:57 +00:00
Mathieu Pellerin
8c7edbeec2 Rename rotation enum keys 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
ce2c8ac21f Address review 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
faf4afcba7 Fix case warning 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
4bad76d876 Where did my pre-commit hook go? 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
1f6c1277dd Lock rotation handle UI/UX behind an enabled boolean 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
ae463d8b75 Implement a ctrl modifier to snap to common angles when rotating item(s) 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
fb0d20942d Show rotation (delta) value in status bar 2025-07-02 11:38:05 +12:00
Mathieu Pellerin
1680a6cd2e [layouts] Brand new layout item rotation handles 2025-07-02 11:38:05 +12:00
Juergen E. Fischer
6fd32d92fd fix #62478 (followup eac401c009) 2025-07-01 17:54:58 +02:00
Till Frankenbach
4c7ae43e46 Fix mExtentWidget not defined in .h 2025-06-21 19:01:36 +02:00
Till Frankenbach
56e5fa40cc Add QgsExtentWidget programatically and set to expanded style 2025-06-21 17:22:11 +02:00
Till Frankenbach
4a01df3c39 Rename method setMainCanvasOpensAtProjectExtent to setRestoreProjectExtentOnProjectLoad for clarity 2025-06-21 16:50:12 +02:00
Till Frankenbach
6aa165ade6 Rename mLoadProjectExtent to mRestoreProjectExtentOnProjectLoad 2025-06-21 13:46:20 +02:00
Till Frankenbach
161de03c78 Fix typos
Co-authored-by: Mathieu Pellerin <nirvn.asia@gmail.com>
2025-06-21 13:46:20 +02:00
Till Frankenbach
58e6943ec4 Adapt UI description 2025-06-21 13:46:20 +02:00
Till Frankenbach
b484c77e35 Rename canvasUseProjectExtent methods to mainCanvasOpensAtProjectExtent for clarity 2025-06-21 13:46:19 +02:00
Till Frankenbach
df7ad50b17 Add option to load project extent when project is loaded 2025-06-21 13:46:19 +02:00
21 changed files with 510 additions and 75 deletions

View File

@ -11505,6 +11505,10 @@ Qgis.MouseHandlesAction.ResizeLeftUp.__doc__ = "Resize left up (Top left handle)
Qgis.MouseHandlesAction.ResizeRightUp.__doc__ = "Resize right up (Top right handle)" Qgis.MouseHandlesAction.ResizeRightUp.__doc__ = "Resize right up (Top right handle)"
Qgis.MouseHandlesAction.ResizeLeftDown.__doc__ = "Resize left down (Bottom left handle)" Qgis.MouseHandlesAction.ResizeLeftDown.__doc__ = "Resize left down (Bottom left handle)"
Qgis.MouseHandlesAction.ResizeRightDown.__doc__ = "Resize right down (Bottom right handle)" Qgis.MouseHandlesAction.ResizeRightDown.__doc__ = "Resize right down (Bottom right handle)"
Qgis.MouseHandlesAction.RotateTopLeft.__doc__ = "Rotate from top left handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.RotateTopRight.__doc__ = "Rotate from top right handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.RotateBottomLeft.__doc__ = "Rotate from bottom left handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.RotateBottomRight.__doc__ = "Rotate right bottom right handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.SelectItem.__doc__ = "Select item" Qgis.MouseHandlesAction.SelectItem.__doc__ = "Select item"
Qgis.MouseHandlesAction.NoAction.__doc__ = "No action" Qgis.MouseHandlesAction.NoAction.__doc__ = "No action"
Qgis.MouseHandlesAction.__doc__ = """Action to be performed by the mouse handles Qgis.MouseHandlesAction.__doc__ = """Action to be performed by the mouse handles
@ -11520,6 +11524,22 @@ Qgis.MouseHandlesAction.__doc__ = """Action to be performed by the mouse handles
* ``ResizeRightUp``: Resize right up (Top right handle) * ``ResizeRightUp``: Resize right up (Top right handle)
* ``ResizeLeftDown``: Resize left down (Bottom left handle) * ``ResizeLeftDown``: Resize left down (Bottom left handle)
* ``ResizeRightDown``: Resize right down (Bottom right handle) * ``ResizeRightDown``: Resize right down (Bottom right handle)
* ``RotateTopLeft``: Rotate from top left handle.
.. versionadded:: 4.0
* ``RotateTopRight``: Rotate from top right handle.
.. versionadded:: 4.0
* ``RotateBottomLeft``: Rotate from bottom left handle.
.. versionadded:: 4.0
* ``RotateBottomRight``: Rotate right bottom right handle.
.. versionadded:: 4.0
* ``SelectItem``: Select item * ``SelectItem``: Select item
* ``NoAction``: No action * ``NoAction``: No action

View File

@ -95,6 +95,22 @@ should be calculated based on the layers in the project.
.. seealso:: :py:func:`fullExtent` .. seealso:: :py:func:`fullExtent`
.. versionadded:: 3.18 .. versionadded:: 3.18
%End
void setRestoreProjectExtentOnProjectLoad( bool state );
%Docstring
Sets whether the project's preset full extent should be restored when
the project is loaded.
.. versionadded:: 3.44
%End
bool mainCanvasOpensAtProjectExtent();
%Docstring
Returns whether the project's preset full extent should be restored when
the project is loaded.
.. versionadded:: 3.44
%End %End
QgsReferencedRectangle fullExtent() const; QgsReferencedRectangle fullExtent() const;

View File

@ -3347,6 +3347,10 @@ The development version
ResizeRightUp, ResizeRightUp,
ResizeLeftDown, ResizeLeftDown,
ResizeRightDown, ResizeRightDown,
RotateTopLeft,
RotateTopRight,
RotateBottomLeft,
RotateBottomRight,
SelectItem, SelectItem,
NoAction NoAction
}; };

View File

@ -1,14 +1,14 @@
Qgis.defaultProjectScales: src/core/qgis.h#L6068 Qgis.defaultProjectScales: src/core/qgis.h#L6072
Qgis.devVersion: src/core/qgis.h#L89 Qgis.devVersion: src/core/qgis.h#L89
Qgis.geoNone: src/core/qgis.h#L6113 Qgis.geoNone: src/core/qgis.h#L6117
Qgis.geoProj4: src/core/qgis.h#L6143 Qgis.geoProj4: src/core/qgis.h#L6147
Qgis.geoWkt: src/core/qgis.h#L6134 Qgis.geoWkt: src/core/qgis.h#L6138
Qgis.geographicCrsAuthId: src/core/qgis.h#L6123 Qgis.geographicCrsAuthId: src/core/qgis.h#L6127
Qgis.geosVersion: src/core/qgis.h#L6103 Qgis.geosVersion: src/core/qgis.h#L6107
Qgis.geosVersionInt: src/core/qgis.h#L6075 Qgis.geosVersionInt: src/core/qgis.h#L6079
Qgis.geosVersionMajor: src/core/qgis.h#L6082 Qgis.geosVersionMajor: src/core/qgis.h#L6086
Qgis.geosVersionMinor: src/core/qgis.h#L6089 Qgis.geosVersionMinor: src/core/qgis.h#L6093
Qgis.geosVersionPatch: src/core/qgis.h#L6096 Qgis.geosVersionPatch: src/core/qgis.h#L6100
Qgis.releaseName: src/core/qgis.h#L79 Qgis.releaseName: src/core/qgis.h#L79
Qgis.version: src/core/qgis.h#L65 Qgis.version: src/core/qgis.h#L65
Qgis.versionInt: src/core/qgis.h#L72 Qgis.versionInt: src/core/qgis.h#L72

View File

@ -11411,6 +11411,10 @@ Qgis.MouseHandlesAction.ResizeLeftUp.__doc__ = "Resize left up (Top left handle)
Qgis.MouseHandlesAction.ResizeRightUp.__doc__ = "Resize right up (Top right handle)" Qgis.MouseHandlesAction.ResizeRightUp.__doc__ = "Resize right up (Top right handle)"
Qgis.MouseHandlesAction.ResizeLeftDown.__doc__ = "Resize left down (Bottom left handle)" Qgis.MouseHandlesAction.ResizeLeftDown.__doc__ = "Resize left down (Bottom left handle)"
Qgis.MouseHandlesAction.ResizeRightDown.__doc__ = "Resize right down (Bottom right handle)" Qgis.MouseHandlesAction.ResizeRightDown.__doc__ = "Resize right down (Bottom right handle)"
Qgis.MouseHandlesAction.RotateTopLeft.__doc__ = "Rotate from top left handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.RotateTopRight.__doc__ = "Rotate from top right handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.RotateBottomLeft.__doc__ = "Rotate from bottom left handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.RotateBottomRight.__doc__ = "Rotate right bottom right handle. \n.. versionadded:: 4.0"
Qgis.MouseHandlesAction.SelectItem.__doc__ = "Select item" Qgis.MouseHandlesAction.SelectItem.__doc__ = "Select item"
Qgis.MouseHandlesAction.NoAction.__doc__ = "No action" Qgis.MouseHandlesAction.NoAction.__doc__ = "No action"
Qgis.MouseHandlesAction.__doc__ = """Action to be performed by the mouse handles Qgis.MouseHandlesAction.__doc__ = """Action to be performed by the mouse handles
@ -11426,6 +11430,22 @@ Qgis.MouseHandlesAction.__doc__ = """Action to be performed by the mouse handles
* ``ResizeRightUp``: Resize right up (Top right handle) * ``ResizeRightUp``: Resize right up (Top right handle)
* ``ResizeLeftDown``: Resize left down (Bottom left handle) * ``ResizeLeftDown``: Resize left down (Bottom left handle)
* ``ResizeRightDown``: Resize right down (Bottom right handle) * ``ResizeRightDown``: Resize right down (Bottom right handle)
* ``RotateTopLeft``: Rotate from top left handle.
.. versionadded:: 4.0
* ``RotateTopRight``: Rotate from top right handle.
.. versionadded:: 4.0
* ``RotateBottomLeft``: Rotate from bottom left handle.
.. versionadded:: 4.0
* ``RotateBottomRight``: Rotate right bottom right handle.
.. versionadded:: 4.0
* ``SelectItem``: Select item * ``SelectItem``: Select item
* ``NoAction``: No action * ``NoAction``: No action

View File

@ -95,6 +95,22 @@ should be calculated based on the layers in the project.
.. seealso:: :py:func:`fullExtent` .. seealso:: :py:func:`fullExtent`
.. versionadded:: 3.18 .. versionadded:: 3.18
%End
void setRestoreProjectExtentOnProjectLoad( bool state );
%Docstring
Sets whether the project's preset full extent should be restored when
the project is loaded.
.. versionadded:: 3.44
%End
bool mainCanvasOpensAtProjectExtent();
%Docstring
Returns whether the project's preset full extent should be restored when
the project is loaded.
.. versionadded:: 3.44
%End %End
QgsReferencedRectangle fullExtent() const; QgsReferencedRectangle fullExtent() const;

View File

@ -3347,6 +3347,10 @@ The development version
ResizeRightUp, ResizeRightUp,
ResizeLeftDown, ResizeLeftDown,
ResizeRightDown, ResizeRightDown,
RotateTopLeft,
RotateTopRight,
RotateBottomLeft,
RotateBottomRight,
SelectItem, SelectItem,
NoAction NoAction
}; };

View File

@ -1,14 +1,14 @@
Qgis.defaultProjectScales: src/core/qgis.h#L6068 Qgis.defaultProjectScales: src/core/qgis.h#L6072
Qgis.devVersion: src/core/qgis.h#L89 Qgis.devVersion: src/core/qgis.h#L89
Qgis.geoNone: src/core/qgis.h#L6113 Qgis.geoNone: src/core/qgis.h#L6117
Qgis.geoProj4: src/core/qgis.h#L6143 Qgis.geoProj4: src/core/qgis.h#L6147
Qgis.geoWkt: src/core/qgis.h#L6134 Qgis.geoWkt: src/core/qgis.h#L6138
Qgis.geographicCrsAuthId: src/core/qgis.h#L6123 Qgis.geographicCrsAuthId: src/core/qgis.h#L6127
Qgis.geosVersion: src/core/qgis.h#L6103 Qgis.geosVersion: src/core/qgis.h#L6107
Qgis.geosVersionInt: src/core/qgis.h#L6075 Qgis.geosVersionInt: src/core/qgis.h#L6079
Qgis.geosVersionMajor: src/core/qgis.h#L6082 Qgis.geosVersionMajor: src/core/qgis.h#L6086
Qgis.geosVersionMinor: src/core/qgis.h#L6089 Qgis.geosVersionMinor: src/core/qgis.h#L6093
Qgis.geosVersionPatch: src/core/qgis.h#L6096 Qgis.geosVersionPatch: src/core/qgis.h#L6100
Qgis.releaseName: src/core/qgis.h#L79 Qgis.releaseName: src/core/qgis.h#L79
Qgis.version: src/core/qgis.h#L65 Qgis.version: src/core/qgis.h#L65
Qgis.versionInt: src/core/qgis.h#L72 Qgis.versionInt: src/core/qgis.h#L72

View File

@ -658,6 +658,9 @@ class Repositories(QObject):
.text() .text()
.strip() .strip()
) )
supports_qt6 = pluginNodes.item(i).firstChildElement(
"supports_qt6"
).text().strip().upper() in ["TRUE", "YES"]
if not qgisMaximumVersion: if not qgisMaximumVersion:
if qgisMinimumVersion[0] == "3" and supports_qt6: if qgisMinimumVersion[0] == "3" and supports_qt6:
qgisMaximumVersion = "4.99" qgisMaximumVersion = "4.99"

View File

@ -98,8 +98,17 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
setupUi( this ); setupUi( this );
mExtentGroupBox->setTitleBase( tr( "Set Project Full Extent" ) ); mExtentGroupBox->setObjectName( "mExtentGroupBox" );
mExtentGroupBox->setMapCanvas( mapCanvas, false ); mExtentGroupBox->setCheckable( true );
mExtentWidget = new QgsExtentWidget( parent, QgsExtentWidget::ExpandedStyle );
QVBoxLayout *mExtentGroupBoxLayout = qobject_cast<QVBoxLayout *>( mExtentGroupBox->layout() );
mExtentGroupBoxLayout->insertWidget( 0, mExtentWidget );
mExtentWidget->setMapCanvas( mapCanvas, false );
mCheckBoxLoadProjectExtent->setObjectName( "mCheckBoxLoadProjectExtent" );
mAdvertisedExtentServer->setOutputCrs( QgsProject::instance()->crs() ); mAdvertisedExtentServer->setOutputCrs( QgsProject::instance()->crs() );
mAdvertisedExtentServer->setMapCanvas( mapCanvas, false ); mAdvertisedExtentServer->setMapCanvas( mapCanvas, false );
@ -476,12 +485,13 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
grpProjectScales->setChecked( QgsProject::instance()->viewSettings()->useProjectScales() ); grpProjectScales->setChecked( QgsProject::instance()->viewSettings()->useProjectScales() );
const QgsReferencedRectangle presetExtent = QgsProject::instance()->viewSettings()->presetFullExtent(); const QgsReferencedRectangle presetExtent = QgsProject::instance()->viewSettings()->presetFullExtent();
mExtentGroupBox->setOutputCrs( QgsProject::instance()->crs() ); mExtentWidget->setOutputCrs( QgsProject::instance()->crs() );
if ( presetExtent.isNull() ) if ( presetExtent.isNull() )
mExtentGroupBox->setOutputExtentFromUser( QgsProject::instance()->viewSettings()->fullExtent(), QgsProject::instance()->crs() ); mExtentWidget->setOutputExtentFromUser( QgsProject::instance()->viewSettings()->fullExtent(), QgsProject::instance()->crs() );
else else
mExtentGroupBox->setOutputExtentFromUser( presetExtent, presetExtent.crs() ); mExtentWidget->setOutputExtentFromUser( presetExtent, presetExtent.crs() );
mExtentGroupBox->setChecked( !presetExtent.isNull() ); mExtentGroupBox->setChecked( !presetExtent.isNull() );
mCheckBoxLoadProjectExtent->setChecked( QgsProject::instance()->viewSettings()->mainCanvasOpensAtProjectExtent() );
mLayerCapabilitiesModel = new QgsLayerCapabilitiesModel( QgsProject::instance(), this ); mLayerCapabilitiesModel = new QgsLayerCapabilitiesModel( QgsProject::instance(), this );
mLayerCapabilitiesModel->setLayerTreeModel( new QgsLayerTreeModel( QgsProject::instance()->layerTreeRoot(), mLayerCapabilitiesModel ) ); mLayerCapabilitiesModel->setLayerTreeModel( new QgsLayerTreeModel( QgsProject::instance()->layerTreeRoot(), mLayerCapabilitiesModel ) );
@ -1209,6 +1219,7 @@ void QgsProjectProperties::apply()
} }
} }
mMapCanvas->enableMapTileRendering( mMapTileRenderingCheckBox->isChecked() ); mMapCanvas->enableMapTileRendering( mMapTileRenderingCheckBox->isChecked() );
QgsProject::instance()->writeEntry( QStringLiteral( "RenderMapTile" ), QStringLiteral( "/" ), mMapTileRenderingCheckBox->isChecked() ); QgsProject::instance()->writeEntry( QStringLiteral( "RenderMapTile" ), QStringLiteral( "/" ), mMapTileRenderingCheckBox->isChecked() );
@ -1350,13 +1361,15 @@ void QgsProjectProperties::apply()
if ( mExtentGroupBox->isChecked() ) if ( mExtentGroupBox->isChecked() )
{ {
QgsProject::instance()->viewSettings()->setPresetFullExtent( QgsReferencedRectangle( mExtentGroupBox->outputExtent(), mExtentGroupBox->outputCrs() ) ); QgsProject::instance()->viewSettings()->setPresetFullExtent( QgsReferencedRectangle( mExtentWidget->outputExtent(), mExtentWidget->outputCrs() ) );
} }
else else
{ {
QgsProject::instance()->viewSettings()->setPresetFullExtent( QgsReferencedRectangle() ); QgsProject::instance()->viewSettings()->setPresetFullExtent( QgsReferencedRectangle() );
} }
QgsProject::instance()->viewSettings()->setRestoreProjectExtentOnProjectLoad( mCheckBoxLoadProjectExtent->isChecked() );
bool isDirty = false; bool isDirty = false;
const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers(); const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
for ( QMap<QString, QgsMapLayer *>::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it ) for ( QMap<QString, QgsMapLayer *>::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it )
@ -2043,7 +2056,7 @@ void QgsProjectProperties::crsChanged( const QgsCoordinateReferenceSystem &crs )
cmbEllipsoid->setEnabled( false ); cmbEllipsoid->setEnabled( false );
} }
mExtentGroupBox->setOutputCrs( crs ); mExtentWidget->setOutputCrs( crs );
mAdvertisedExtentServer->setOutputCrs( crs ); mAdvertisedExtentServer->setOutputCrs( crs );
} }

View File

@ -250,6 +250,8 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui:
QDoubleSpinBox *mWMSDefaultMapUnitsPerMm = nullptr; QDoubleSpinBox *mWMSDefaultMapUnitsPerMm = nullptr;
QgsScaleWidget *mWMSDefaultMapUnitScale = nullptr; QgsScaleWidget *mWMSDefaultMapUnitScale = nullptr;
QgsExtentWidget *mExtentWidget;
QgsCoordinateReferenceSystem mCrs; QgsCoordinateReferenceSystem mCrs;

View File

@ -20,10 +20,13 @@
#include "qgsmaplayerutils.h" #include "qgsmaplayerutils.h"
#include "qgscoordinatetransform.h" #include "qgscoordinatetransform.h"
#include <QDomElement> #include <QDomElement>
#include "qgsmessagelog.h"
QgsProjectViewSettings::QgsProjectViewSettings( QgsProject *project ) QgsProjectViewSettings::QgsProjectViewSettings( QgsProject *project )
: QObject( project ) : QObject( project )
, mProject( project ) , mProject( project )
, mRestoreProjectExtentOnProjectLoad( false )
{ {
} }
@ -71,6 +74,17 @@ void QgsProjectViewSettings::setPresetFullExtent( const QgsReferencedRectangle &
emit presetFullExtentChanged(); emit presetFullExtentChanged();
} }
void QgsProjectViewSettings::setRestoreProjectExtentOnProjectLoad( bool projectExtentCheckboxState )
{
mRestoreProjectExtentOnProjectLoad = projectExtentCheckboxState;
}
bool QgsProjectViewSettings::mainCanvasOpensAtProjectExtent( )
{
return mRestoreProjectExtentOnProjectLoad;
}
QgsReferencedRectangle QgsProjectViewSettings::fullExtent() const QgsReferencedRectangle QgsProjectViewSettings::fullExtent() const
{ {
if ( !mProject ) if ( !mProject )
@ -198,6 +212,7 @@ bool QgsProjectViewSettings::readXml( const QDomElement &element, const QgsReadW
} }
mDefaultRotation = element.attribute( QStringLiteral( "rotation" ), QStringLiteral( "0" ) ).toDouble(); mDefaultRotation = element.attribute( QStringLiteral( "rotation" ), QStringLiteral( "0" ) ).toDouble();
mRestoreProjectExtentOnProjectLoad = element.attribute( QStringLiteral( "LoadProjectExtent" ), QStringLiteral( "0" ) ).toInt();
return true; return true;
} }
@ -207,6 +222,8 @@ QDomElement QgsProjectViewSettings::writeXml( QDomDocument &doc, const QgsReadWr
QDomElement element = doc.createElement( QStringLiteral( "ProjectViewSettings" ) ); QDomElement element = doc.createElement( QStringLiteral( "ProjectViewSettings" ) );
element.setAttribute( QStringLiteral( "UseProjectScales" ), mUseProjectScales ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); element.setAttribute( QStringLiteral( "UseProjectScales" ), mUseProjectScales ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
element.setAttribute( QStringLiteral( "LoadProjectExtent" ), mRestoreProjectExtentOnProjectLoad ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
element.setAttribute( QStringLiteral( "rotation" ), qgsDoubleToString( mDefaultRotation ) ); element.setAttribute( QStringLiteral( "rotation" ), qgsDoubleToString( mDefaultRotation ) );
QDomElement scales = doc.createElement( QStringLiteral( "Scales" ) ); QDomElement scales = doc.createElement( QStringLiteral( "Scales" ) );

View File

@ -104,6 +104,20 @@ class CORE_EXPORT QgsProjectViewSettings : public QObject
*/ */
void setPresetFullExtent( const QgsReferencedRectangle &extent ); void setPresetFullExtent( const QgsReferencedRectangle &extent );
/**
* Sets whether the project's preset full extent should be restored when the project is loaded.
*
* \since QGIS 3.44
*/
void setRestoreProjectExtentOnProjectLoad( bool state );
/**
* Returns whether the project's preset full extent should be restored when the project is loaded.
*
* \since QGIS 3.44
*/
bool mainCanvasOpensAtProjectExtent();
/** /**
* Returns the full extent of the project, which represents the maximal limits of the project. * Returns the full extent of the project, which represents the maximal limits of the project.
* *
@ -228,6 +242,7 @@ class CORE_EXPORT QgsProjectViewSettings : public QObject
bool mUseProjectScales = false; bool mUseProjectScales = false;
QgsReferencedRectangle mDefaultViewExtent; QgsReferencedRectangle mDefaultViewExtent;
QgsReferencedRectangle mPresetFullExtent; QgsReferencedRectangle mPresetFullExtent;
bool mRestoreProjectExtentOnProjectLoad;
double mDefaultRotation = 0; double mDefaultRotation = 0;
}; };

View File

@ -5897,6 +5897,10 @@ class CORE_EXPORT Qgis
ResizeRightUp, //!< Resize right up (Top right handle) ResizeRightUp, //!< Resize right up (Top right handle)
ResizeLeftDown, //!< Resize left down (Bottom left handle) ResizeLeftDown, //!< Resize left down (Bottom left handle)
ResizeRightDown, //!< Resize right down (Bottom right handle) ResizeRightDown, //!< Resize right down (Bottom right handle)
RotateTopLeft, //!< Rotate from top left handle. \since QGIS 4.0
RotateTopRight, //!< Rotate from top right handle. \since QGIS 4.0
RotateBottomLeft, //!< Rotate from bottom left handle. \since QGIS 4.0
RotateBottomRight, //!< Rotate right bottom right handle. \since QGIS 4.0
SelectItem, //!< Select item SelectItem, //!< Select item
NoAction //!< No action NoAction //!< No action
}; };

View File

@ -196,6 +196,10 @@ void QgsLayoutGuiUtils::registerGuiForKnownItemTypes( QgsMapCanvas *mapCanvas )
case Qgis::MouseHandlesAction::MoveItem: case Qgis::MouseHandlesAction::MoveItem:
case Qgis::MouseHandlesAction::NoAction: case Qgis::MouseHandlesAction::NoAction:
case Qgis::MouseHandlesAction::SelectItem: case Qgis::MouseHandlesAction::SelectItem:
case Qgis::MouseHandlesAction::RotateTopLeft:
case Qgis::MouseHandlesAction::RotateTopRight:
case Qgis::MouseHandlesAction::RotateBottomLeft:
case Qgis::MouseHandlesAction::RotateBottomRight:
return; return;
case Qgis::MouseHandlesAction::ResizeUp: case Qgis::MouseHandlesAction::ResizeUp:

View File

@ -44,6 +44,8 @@ QgsLayoutMouseHandles::QgsLayoutMouseHandles( QgsLayout *layout, QgsLayoutView *
, mLayout( layout ) , mLayout( layout )
, mView( view ) , mView( view )
{ {
setRotationEnabled( true );
//listen for selection changes, and update handles accordingly //listen for selection changes, and update handles accordingly
connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged ); connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged );
@ -251,6 +253,16 @@ void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double
qgis::down_cast<QgsLayoutItem *>( item )->attemptMoveBy( deltaX, deltaY ); qgis::down_cast<QgsLayoutItem *>( item )->attemptMoveBy( deltaX, deltaY );
} }
void QgsLayoutMouseHandles::rotateItem( QGraphicsItem *item, double deltaDegree, double deltaCenterX, double deltaCenterY )
{
QgsLayoutItem *itm = qgis::down_cast<QgsLayoutItem *>( item );
QgsLayoutItem::ReferencePoint previousReferencePoint = itm->referencePoint();
itm->setReferencePoint( QgsLayoutItem::Middle );
itm->attemptMoveBy( deltaCenterX, deltaCenterY );
itm->setItemRotation( itm->itemRotation() + deltaDegree, true );
itm->setReferencePoint( previousReferencePoint );
}
void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect ) void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
{ {
QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ); QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );

View File

@ -74,6 +74,7 @@ class GUI_EXPORT QgsLayoutMouseHandles : public QgsGraphicsViewMouseHandles
void expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const override; void expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const override;
void expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const; void expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const;
void moveItem( QGraphicsItem *item, double deltaX, double deltaY ) override; void moveItem( QGraphicsItem *item, double deltaX, double deltaY ) override;
void rotateItem( QGraphicsItem *item, double deltaDegree, double deltaCenterX, double deltaCenterY ) override;
void setItemRect( QGraphicsItem *item, QRectF rect ) override; void setItemRect( QGraphicsItem *item, QRectF rect ) override;
void showStatusMessage( const QString &message ) override; void showStatusMessage( const QString &message ) override;
void hideAlignItems() override; void hideAlignItems() override;

View File

@ -15,10 +15,13 @@
* * * *
***************************************************************************/ ***************************************************************************/
#include "qgsgraphicsviewmousehandles.h"
#include "moc_qgsgraphicsviewmousehandles.cpp" #include "moc_qgsgraphicsviewmousehandles.cpp"
#include "qgsrendercontext.h"
#include "qgis.h" #include "qgis.h"
#include "qgsgraphicsviewmousehandles.h"
#include "qgslayoututils.h"
#include "qgsrendercontext.h"
#include <QGraphicsView> #include <QGraphicsView>
#include <QGraphicsSceneHoverEvent> #include <QGraphicsSceneHoverEvent>
#include <QPainter> #include <QPainter>
@ -34,6 +37,28 @@ QgsGraphicsViewMouseHandles::QgsGraphicsViewMouseHandles( QGraphicsView *view )
{ {
//accept hover events, required for changing cursor to resize cursors //accept hover events, required for changing cursor to resize cursors
setAcceptHoverEvents( true ); setAcceptHoverEvents( true );
//prepare rotation handle path
mRotationHandlePath.moveTo( 0, 14 );
mRotationHandlePath.lineTo( 6, 20 );
mRotationHandlePath.lineTo( 12, 14 );
mRotationHandlePath.arcTo( 8, 8, 12, 12, 180, -90 );
mRotationHandlePath.lineTo( 14, 12 );
mRotationHandlePath.lineTo( 20, 6 );
mRotationHandlePath.lineTo( 14, 0 );
mRotationHandlePath.arcTo( 4, 4, 20, 20, 90, 90 );
mRotationHandlePath.lineTo( 0, 14 );
}
void QgsGraphicsViewMouseHandles::setRotationEnabled( bool enable )
{
if ( mRotationEnabled == enable )
{
return;
}
mRotationEnabled = enable;
update();
} }
void QgsGraphicsViewMouseHandles::paintInternal( QPainter *painter, bool showHandles, bool showStaticBoundingBoxes, bool showTemporaryBoundingBoxes, const QStyleOptionGraphicsItem *, QWidget * ) void QgsGraphicsViewMouseHandles::paintInternal( QPainter *painter, bool showHandles, bool showStaticBoundingBoxes, bool showTemporaryBoundingBoxes, const QStyleOptionGraphicsItem *, QWidget * )
@ -62,6 +87,11 @@ QRectF QgsGraphicsViewMouseHandles::storedItemRect( QGraphicsItem *item ) const
return itemRect( item ); return itemRect( item );
} }
void QgsGraphicsViewMouseHandles::rotateItem( QGraphicsItem *, double, double, double )
{
QgsDebugError( QStringLiteral( "Rotation is not implemented for this class" ) );
}
void QgsGraphicsViewMouseHandles::previewItemMove( QGraphicsItem *, double, double ) void QgsGraphicsViewMouseHandles::previewItemMove( QGraphicsItem *, double, double )
{ {
} }
@ -108,7 +138,7 @@ void QgsGraphicsViewMouseHandles::drawHandles( QPainter *painter, double rectHan
painter->setBrush( Qt::NoBrush ); painter->setBrush( Qt::NoBrush );
painter->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) ); painter->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
//draw resize handles, using a filled white box //draw resize handles, using filled white boxes
painter->setBrush( QColor( 255, 255, 255, 255 ) ); painter->setBrush( QColor( 255, 255, 255, 255 ) );
//top left //top left
painter->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) ); painter->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) );
@ -126,6 +156,63 @@ void QgsGraphicsViewMouseHandles::drawHandles( QPainter *painter, double rectHan
painter->drawRect( QRectF( ( rect().width() - rectHandlerSize ) / 2, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) ); painter->drawRect( QRectF( ( rect().width() - rectHandlerSize ) / 2, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
//bottom right //bottom right
painter->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) ); painter->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
if ( isRotationEnabled() )
{
//draw rotate handles
const double scale = rectHandlerSize / mHandleSize;
const bool drawBottomRotationHandles = ( rectHandlerSize * 2 ) + ( mRotationHandleSize * scale * 2 ) < rect().height();
const bool drawRightRotationHandles = ( rectHandlerSize * 2 ) + ( mRotationHandleSize * scale * 2 ) < rect().width();
QTransform transform;
//top left
transform.reset();
transform.translate( rectHandlerSize, rectHandlerSize );
transform.scale( scale, scale );
painter->save();
painter->setTransform( transform, true );
painter->drawPath( mRotationHandlePath );
painter->restore();
//top right
if ( drawRightRotationHandles )
{
transform.reset();
transform.translate( rect().width() - rectHandlerSize, rectHandlerSize );
transform.rotate( 90 );
transform.scale( scale, scale );
painter->save();
painter->setTransform( transform, true );
painter->drawPath( mRotationHandlePath );
painter->restore();
}
if ( drawBottomRotationHandles )
{
//bottom left
transform.reset();
transform.translate( rectHandlerSize, rect().height() - rectHandlerSize );
transform.rotate( 270 );
transform.scale( scale, scale );
painter->save();
painter->setTransform( transform, true );
painter->drawPath( mRotationHandlePath );
painter->restore();
}
if ( drawBottomRotationHandles && drawRightRotationHandles )
{
//bottom right
transform.reset();
transform.translate( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize );
transform.rotate( 180 );
transform.scale( scale, scale );
painter->save();
painter->setTransform( transform, true );
painter->drawPath( mRotationHandlePath );
painter->restore();
}
}
} }
void QgsGraphicsViewMouseHandles::drawSelectedItemBounds( QPainter *painter ) void QgsGraphicsViewMouseHandles::drawSelectedItemBounds( QPainter *painter )
@ -181,6 +268,17 @@ void QgsGraphicsViewMouseHandles::drawSelectedItemBounds( QPainter *painter )
relativeResizeRect( thisItemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect ); relativeResizeRect( thisItemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
itemBounds = QPolygonF( thisItemRect ); itemBounds = QPolygonF( thisItemRect );
} }
else if ( isRotating() && !itemIsLocked( item ) )
{
const QPolygonF itemSceneBounds = item->mapToScene( itemRect( item ) );
const QPointF rotationCenter = sceneTransform().map( rect().center() );
QTransform transform;
transform.translate( rotationCenter.x(), rotationCenter.y() );
transform.rotate( mRotationDelta );
transform.translate( -rotationCenter.x(), -rotationCenter.y() );
itemBounds = mapFromScene( transform.map( itemSceneBounds ) );
}
else else
{ {
// not resizing or moving, so just map the item's bounds to the mouse handle item's coordinate system // not resizing or moving, so just map the item's bounds to the mouse handle item's coordinate system
@ -195,7 +293,7 @@ void QgsGraphicsViewMouseHandles::drawSelectedItemBounds( QPainter *painter )
} }
} }
double QgsGraphicsViewMouseHandles::rectHandlerBorderTolerance() double QgsGraphicsViewMouseHandles::rectHandlerBorderTolerance() const
{ {
if ( !mView ) if ( !mView )
return 0; return 0;
@ -312,6 +410,12 @@ Qt::CursorShape QgsGraphicsViewMouseHandles::cursorForPosition( QPointF itemCoor
} }
case Qgis::MouseHandlesAction::SelectItem: case Qgis::MouseHandlesAction::SelectItem:
return Qt::ArrowCursor; return Qt::ArrowCursor;
case Qgis::MouseHandlesAction::RotateTopLeft:
case Qgis::MouseHandlesAction::RotateTopRight:
case Qgis::MouseHandlesAction::RotateBottomLeft:
case Qgis::MouseHandlesAction::RotateBottomRight:
return Qt::PointingHandCursor;
} }
return Qt::ArrowCursor; return Qt::ArrowCursor;
@ -324,8 +428,14 @@ Qgis::MouseHandlesAction QgsGraphicsViewMouseHandles::mouseActionForPosition( QP
bool nearLowerBorder = false; bool nearLowerBorder = false;
bool nearUpperBorder = false; bool nearUpperBorder = false;
bool nearLeftInner = false;
bool nearRightInner = false;
bool nearLowerInner = false;
bool nearUpperInner = false;
bool withinWidth = false; bool withinWidth = false;
bool withinHeight = false; bool withinHeight = false;
if ( itemCoordPos.x() >= 0 && itemCoordPos.x() <= rect().width() ) if ( itemCoordPos.x() >= 0 && itemCoordPos.x() <= rect().width() )
{ {
withinWidth = true; withinWidth = true;
@ -336,23 +446,40 @@ Qgis::MouseHandlesAction QgsGraphicsViewMouseHandles::mouseActionForPosition( QP
} }
double borderTolerance = rectHandlerBorderTolerance(); double borderTolerance = rectHandlerBorderTolerance();
double innerTolerance = mRotationHandleSize * borderTolerance / mHandleSize;
if ( itemCoordPos.x() >= 0 && itemCoordPos.x() < borderTolerance ) if ( itemCoordPos.x() >= 0 && itemCoordPos.x() < borderTolerance )
{ {
nearLeftBorder = true; nearLeftBorder = true;
} }
else if ( isRotationEnabled() && itemCoordPos.x() >= borderTolerance && itemCoordPos.x() < ( borderTolerance + innerTolerance ) )
{
nearLeftInner = true;
}
if ( itemCoordPos.y() >= 0 && itemCoordPos.y() < borderTolerance ) if ( itemCoordPos.y() >= 0 && itemCoordPos.y() < borderTolerance )
{ {
nearUpperBorder = true; nearUpperBorder = true;
} }
else if ( isRotationEnabled() && itemCoordPos.y() >= borderTolerance && itemCoordPos.y() < ( borderTolerance + innerTolerance ) )
{
nearUpperInner = true;
}
if ( itemCoordPos.x() <= rect().width() && itemCoordPos.x() > ( rect().width() - borderTolerance ) ) if ( itemCoordPos.x() <= rect().width() && itemCoordPos.x() > ( rect().width() - borderTolerance ) )
{ {
nearRightBorder = true; nearRightBorder = true;
} }
else if ( isRotationEnabled() && itemCoordPos.x() <= ( rect().width() - borderTolerance ) && itemCoordPos.x() > ( rect().width() - borderTolerance - innerTolerance ) )
{
nearRightInner = true;
}
if ( itemCoordPos.y() <= rect().height() && itemCoordPos.y() > ( rect().height() - borderTolerance ) ) if ( itemCoordPos.y() <= rect().height() && itemCoordPos.y() > ( rect().height() - borderTolerance ) )
{ {
nearLowerBorder = true; nearLowerBorder = true;
} }
else if ( isRotationEnabled() && itemCoordPos.y() <= ( rect().height() - borderTolerance ) && itemCoordPos.y() > ( rect().height() - borderTolerance - innerTolerance ) )
{
nearLowerInner = true;
}
if ( nearLeftBorder && nearUpperBorder ) if ( nearLeftBorder && nearUpperBorder )
{ {
@ -386,6 +513,22 @@ Qgis::MouseHandlesAction QgsGraphicsViewMouseHandles::mouseActionForPosition( QP
{ {
return Qgis::MouseHandlesAction::ResizeDown; return Qgis::MouseHandlesAction::ResizeDown;
} }
else if ( nearLeftInner && nearUpperInner )
{
return Qgis::MouseHandlesAction::RotateTopLeft;
}
else if ( nearRightInner && nearUpperInner )
{
return Qgis::MouseHandlesAction::RotateTopRight;
}
else if ( nearLeftInner && nearLowerInner )
{
return Qgis::MouseHandlesAction::RotateBottomLeft;
}
else if ( nearRightInner && nearLowerInner )
{
return Qgis::MouseHandlesAction::RotateBottomRight;
}
//find out if cursor position is over a selected item //find out if cursor position is over a selected item
QPointF scenePoint = mapToScene( itemCoordPos ); QPointF scenePoint = mapToScene( itemCoordPos );
@ -489,19 +632,42 @@ void QgsGraphicsViewMouseHandles::mousePressEvent( QGraphicsSceneMouseEvent *eve
hideAlignItems(); hideAlignItems();
if ( mCurrentMouseMoveAction == Qgis::MouseHandlesAction::MoveItem ) switch ( mCurrentMouseMoveAction )
{ {
//moving items case Qgis::MouseHandlesAction::MoveItem:
mIsDragging = true; //moving items
} mIsDragging = true;
else if ( mCurrentMouseMoveAction != Qgis::MouseHandlesAction::SelectItem && mCurrentMouseMoveAction != Qgis::MouseHandlesAction::NoAction ) break;
{
//resizing items case Qgis::MouseHandlesAction::ResizeUp:
mIsResizing = true; case Qgis::MouseHandlesAction::ResizeDown:
mResizeRect = QRectF( 0, 0, mBeginHandleWidth, mBeginHandleHeight ); case Qgis::MouseHandlesAction::ResizeLeft:
mResizeMoveX = 0; case Qgis::MouseHandlesAction::ResizeRight:
mResizeMoveY = 0; case Qgis::MouseHandlesAction::ResizeLeftUp:
mCursorOffset = calcCursorEdgeOffset( mMouseMoveStartPos ); case Qgis::MouseHandlesAction::ResizeRightUp:
case Qgis::MouseHandlesAction::ResizeLeftDown:
case Qgis::MouseHandlesAction::ResizeRightDown:
//resizing items
mIsResizing = true;
mResizeRect = QRectF( 0, 0, mBeginHandleWidth, mBeginHandleHeight );
mResizeMoveX = 0;
mResizeMoveY = 0;
mCursorOffset = calcCursorEdgeOffset( mMouseMoveStartPos );
break;
case Qgis::MouseHandlesAction::RotateTopLeft:
case Qgis::MouseHandlesAction::RotateTopRight:
case Qgis::MouseHandlesAction::RotateBottomLeft:
case Qgis::MouseHandlesAction::RotateBottomRight:
mIsRotating = true;
mRotationCenter = sceneTransform().map( rect().center() );
mRotationBegin = std::atan2( mMouseMoveStartPos.y() - mRotationCenter.y(), mMouseMoveStartPos.x() - mRotationCenter.x() ) * 180 / M_PI;
mRotationCurrent = 0.0;
break;
case Qgis::MouseHandlesAction::SelectItem:
case Qgis::MouseHandlesAction::NoAction:
break;
} }
} }
@ -537,6 +703,12 @@ void QgsGraphicsViewMouseHandles::mouseMoveEvent( QGraphicsSceneMouseEvent *even
//resize from center if alt depressed //resize from center if alt depressed
resizeMouseMove( event->lastScenePos(), event->modifiers() & Qt::ShiftModifier, event->modifiers() & Qt::AltModifier ); resizeMouseMove( event->lastScenePos(), event->modifiers() & Qt::ShiftModifier, event->modifiers() & Qt::AltModifier );
} }
else if ( isRotating() )
{
//currently rotating a selection
//snap to common angles if ctrl is pressed
rotateMouseMove( event->lastScenePos(), event->modifiers() & Qt::ControlModifier );
}
} }
void QgsGraphicsViewMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *event ) void QgsGraphicsViewMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *event )
@ -567,12 +739,13 @@ void QgsGraphicsViewMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *e
{ {
mIsDragging = false; mIsDragging = false;
mIsResizing = false; mIsResizing = false;
mIsRotating = false;
update(); update();
hideAlignItems(); hideAlignItems();
return; return;
} }
if ( mCurrentMouseMoveAction == Qgis::MouseHandlesAction::MoveItem ) if ( mIsDragging )
{ {
//move selected items //move selected items
startMacroCommand( tr( "Move Items" ) ); startMacroCommand( tr( "Move Items" ) );
@ -597,8 +770,10 @@ void QgsGraphicsViewMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *e
endItemCommand( item ); endItemCommand( item );
} }
endMacroCommand(); endMacroCommand();
mIsDragging = false;
} }
else if ( mCurrentMouseMoveAction != Qgis::MouseHandlesAction::NoAction ) else if ( mIsResizing )
{ {
//resize selected items //resize selected items
startMacroCommand( tr( "Resize Items" ) ); startMacroCommand( tr( "Resize Items" ) );
@ -635,17 +810,44 @@ void QgsGraphicsViewMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *e
endItemCommand( item ); endItemCommand( item );
} }
endMacroCommand(); endMacroCommand();
mIsResizing = false;
}
else if ( mIsRotating )
{
const QPointF itemRotationCenter = sceneTransform().map( rect().center() );
//move selected items
startMacroCommand( tr( "Rotate Items" ) );
//move all selected items
const QList<QGraphicsItem *> selectedItems = selectedSceneItems( false );
for ( QGraphicsItem *item : selectedItems )
{
if ( itemIsLocked( item ) || ( item->flags() & QGraphicsItem::ItemIsSelectable ) == 0 || itemIsGroupMember( item ) )
{
//don't move locked items, or grouped items (group takes care of that)
continue;
}
const QPointF itemCenter = item->mapToScene( itemRect( item ) ).boundingRect().center();
QTransform transform;
transform.translate( itemRotationCenter.x(), itemRotationCenter.y() );
transform.rotate( mRotationDelta );
transform.translate( -itemRotationCenter.x(), -itemRotationCenter.y() );
const QPointF rotatedItemCenter = transform.map( itemCenter );
createItemCommand( item );
rotateItem( item, mRotationDelta, rotatedItemCenter.x() - itemCenter.x(), rotatedItemCenter.y() - itemCenter.y() );
endItemCommand( item );
}
endMacroCommand();
mIsRotating = false;
} }
hideAlignItems(); hideAlignItems();
if ( mIsDragging )
{
mIsDragging = false;
}
if ( mIsResizing )
{
mIsResizing = false;
}
//reset default action //reset default action
mCurrentMouseMoveAction = Qgis::MouseHandlesAction::MoveItem; mCurrentMouseMoveAction = Qgis::MouseHandlesAction::MoveItem;
@ -721,6 +923,36 @@ void QgsGraphicsViewMouseHandles::updateHandles()
update(); update();
} }
void QgsGraphicsViewMouseHandles::rotateMouseMove( QPointF currentPosition, bool snapToCommonAngles )
{
if ( !scene() )
{
return;
}
mRotationCurrent = std::atan2( currentPosition.y() - mRotationCenter.y(), currentPosition.x() - mRotationCenter.x() ) * 180 / M_PI;
mRotationDelta = mRotationCurrent - mRotationBegin;
if ( snapToCommonAngles )
{
mRotationDelta = QgsLayoutUtils::snappedAngle( mRotationDelta );
}
const double itemRotationRadian = rotation() * M_PI / 180;
const double deltaX = ( rect().width() / 2 ) * cos( itemRotationRadian ) - ( rect().height() / 2 ) * sin( itemRotationRadian );
const double deltaY = ( rect().width() / 2 ) * sin( itemRotationRadian ) + ( rect().height() / 2 ) * cos( itemRotationRadian );
QTransform rotateTransform;
rotateTransform.translate( deltaX, deltaY );
rotateTransform.rotate( mRotationDelta );
rotateTransform.translate( -deltaX, -deltaY );
setTransform( rotateTransform );
//show current selection rotation in status bar
showStatusMessage( tr( "rotation: %1°" ).arg( QString::number( mRotationDelta, 'f', 2 ) ) );
return;
}
void QgsGraphicsViewMouseHandles::dragMouseMove( QPointF currentPosition, bool lockMovement, bool preventSnap ) void QgsGraphicsViewMouseHandles::dragMouseMove( QPointF currentPosition, bool lockMovement, bool preventSnap )
{ {
if ( !scene() ) if ( !scene() )
@ -985,6 +1217,10 @@ void QgsGraphicsViewMouseHandles::resizeMouseMove( QPointF currentPosition, bool
break; break;
} }
case Qgis::MouseHandlesAction::RotateTopLeft:
case Qgis::MouseHandlesAction::RotateTopRight:
case Qgis::MouseHandlesAction::RotateBottomLeft:
case Qgis::MouseHandlesAction::RotateBottomRight:
case Qgis::MouseHandlesAction::MoveItem: case Qgis::MouseHandlesAction::MoveItem:
case Qgis::MouseHandlesAction::SelectItem: case Qgis::MouseHandlesAction::SelectItem:
case Qgis::MouseHandlesAction::NoAction: case Qgis::MouseHandlesAction::NoAction:
@ -1099,6 +1335,10 @@ QSizeF QgsGraphicsViewMouseHandles::calcCursorEdgeOffset( QPointF cursorPos )
case Qgis::MouseHandlesAction::ResizeLeftDown: case Qgis::MouseHandlesAction::ResizeLeftDown:
return QSizeF( sceneMousePos.x(), sceneMousePos.y() - rect().height() ); return QSizeF( sceneMousePos.x(), sceneMousePos.y() - rect().height() );
case Qgis::MouseHandlesAction::RotateTopLeft:
case Qgis::MouseHandlesAction::RotateTopRight:
case Qgis::MouseHandlesAction::RotateBottomLeft:
case Qgis::MouseHandlesAction::RotateBottomRight:
case Qgis::MouseHandlesAction::MoveItem: case Qgis::MouseHandlesAction::MoveItem:
case Qgis::MouseHandlesAction::SelectItem: case Qgis::MouseHandlesAction::SelectItem:
case Qgis::MouseHandlesAction::NoAction: case Qgis::MouseHandlesAction::NoAction:

View File

@ -77,11 +77,37 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
//! Returns TRUE is user is currently resizing with the handles //! Returns TRUE is user is currently resizing with the handles
bool isResizing() const { return mIsResizing; } bool isResizing() const { return mIsResizing; }
/**
* Returns TRUE is user is currently rotating with the handles.
*
* \since QGIS 4.0
*/
bool isRotating() const { return mIsRotating; }
bool shouldBlockEvent( QInputEvent *event ) const; bool shouldBlockEvent( QInputEvent *event ) const;
//! Initializes a drag operation \since QGIS 3.34 //! Initializes a drag operation \since QGIS 3.34
void startMove( QPointF sceneCoordPos ); void startMove( QPointF sceneCoordPos );
/**
* Returns TRUE if rotation functionality is enabled.
*
* Rotation is not enabled by default.
*
* \since QGIS 4.0
*/
bool isRotationEnabled() const { return mRotationEnabled; }
/**
* Sets whether rotation functionality is enabled.
*
* Rotation is not enabled by default. Subclasses must implement the
* rotateItem() method in order to support rotation.
*
* \since QGIS 4.0
*/
void setRotationEnabled( bool enable );
public slots: public slots:
//! Redraws handles when selected item size changes //! Redraws handles when selected item size changes
@ -111,6 +137,7 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
virtual QRectF itemRect( QGraphicsItem *item ) const = 0; virtual QRectF itemRect( QGraphicsItem *item ) const = 0;
virtual QRectF storedItemRect( QGraphicsItem *item ) const; virtual QRectF storedItemRect( QGraphicsItem *item ) const;
virtual void moveItem( QGraphicsItem *item, double deltaX, double deltaY ) = 0; virtual void moveItem( QGraphicsItem *item, double deltaX, double deltaY ) = 0;
virtual void rotateItem( QGraphicsItem *item, double deltaDegree, double deltaCenterX, double deltaCenterY );
virtual void previewItemMove( QGraphicsItem *item, double deltaX, double deltaY ); virtual void previewItemMove( QGraphicsItem *item, double deltaX, double deltaY );
virtual void setItemRect( QGraphicsItem *item, QRectF rect ) = 0; virtual void setItemRect( QGraphicsItem *item, QRectF rect ) = 0;
@ -158,6 +185,9 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
//! Handles resizing of items during mouse move //! Handles resizing of items during mouse move
void resizeMouseMove( QPointF currentPosition, bool lockAspect, bool fromCenter ); void resizeMouseMove( QPointF currentPosition, bool lockAspect, bool fromCenter );
//! Handles rotating of tiems during mouse move
void rotateMouseMove( QPointF currentPosition, bool snapToCommonAngles );
void setHandleSize( double size ); void setHandleSize( double size );
//! Finds out which mouse move action to choose depending on the cursor position inside the widget //! Finds out which mouse move action to choose depending on the cursor position inside the widget
@ -194,6 +224,8 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
QGraphicsView *mView = nullptr; QGraphicsView *mView = nullptr;
double mHandleSize = 10; double mHandleSize = 10;
double mRotationHandleSize = 20;
QPainterPath mRotationHandlePath;
QSizeF mCursorOffset; QSizeF mCursorOffset;
double mResizeMoveX = 0; double mResizeMoveX = 0;
@ -205,6 +237,16 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
QRectF mResizeRect; QRectF mResizeRect;
bool mRotationEnabled = false;
//! Center point around which rotation occurs
QPointF mRotationCenter;
//! The starting rotation angle from center point
double mRotationBegin = 0.0;
//! The current rotation angle from center point
double mRotationCurrent = 0.0;
//! The rotation angle delta to be applied (can be snapped to common angle)
double mRotationDelta = 0.0;
//! Start point of the last mouse move action (in scene coordinates) //! Start point of the last mouse move action (in scene coordinates)
QPointF mMouseMoveStartPos; QPointF mMouseMoveStartPos;
@ -215,6 +257,8 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
bool mIsDragging = false; bool mIsDragging = false;
//! True is user is currently resizing items //! True is user is currently resizing items
bool mIsResizing = false; bool mIsResizing = false;
//! True is user is currently rotating items
bool mIsRotating = false;
//! Position of the mouse at beginning of move/resize (in scene coordinates) //! Position of the mouse at beginning of move/resize (in scene coordinates)
QPointF mBeginMouseEventPos; QPointF mBeginMouseEventPos;
@ -232,7 +276,7 @@ class GUI_EXPORT QgsGraphicsViewMouseHandles : public QObject, public QGraphicsR
* Returns the current (zoom level dependent) tolerance to decide if mouse position is close enough to the * Returns the current (zoom level dependent) tolerance to decide if mouse position is close enough to the
* item border for resizing. * item border for resizing.
*/ */
double rectHandlerBorderTolerance(); double rectHandlerBorderTolerance() const;
//! Finds out the appropriate cursor for the current mouse position in the widget (e.g. move in the middle, resize at border) //! Finds out the appropriate cursor for the current mouse position in the widget (e.g. move in the middle, resize at border)
Qt::CursorShape cursorForPosition( QPointF itemCoordPos ); Qt::CursorShape cursorForPosition( QPointF itemCoordPos );

View File

@ -3271,7 +3271,14 @@ void QgsMapCanvas::readProject( const QDomDocument &doc )
// never manually set the crs for the main canvas - this is instead connected to the project CRS // never manually set the crs for the main canvas - this is instead connected to the project CRS
setDestinationCrs( tmpSettings.destinationCrs() ); setDestinationCrs( tmpSettings.destinationCrs() );
} }
setExtent( tmpSettings.extent() ); if ( QgsProject::instance()->viewSettings()->mainCanvasOpensAtProjectExtent() && objectName() == QLatin1String( "theMapCanvas" ) )
{
zoomToProjectExtent();
}
else
{
setExtent( tmpSettings.extent() );
}
setRotation( tmpSettings.rotation() ); setRotation( tmpSettings.rotation() );
enableMapTileRendering( tmpSettings.testFlag( Qgis::MapSettingsFlag::RenderMapTile ) ); enableMapTileRendering( tmpSettings.testFlag( Qgis::MapSettingsFlag::RenderMapTile ) );

View File

@ -1010,19 +1010,19 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QgsExtentGroupBox" name="mExtentGroupBox"> <widget class="QgsCollapsibleGroupBox" name="mExtentGroupBox">
<property name="toolTip">
<string>If checked the preset extent will be used as the full extent of the project. If unchecked, the project's full extent will be calculated using the full extent of all layers in the project.</string>
</property>
<property name="title"> <property name="title">
<string>Extent (current: none)</string> <string>Set Project Full Extent</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_30">
<item>
<widget class="QCheckBox" name="mCheckBoxLoadProjectExtent">
<property name="text">
<string>Use as extent for the main canvas when opening the project</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
@ -1132,8 +1132,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>528</width> <width>685</width>
<height>85</height> <height>791</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_9"> <layout class="QVBoxLayout" name="verticalLayout_9">
@ -3601,12 +3601,6 @@
<extends>QDateTimeEdit</extends> <extends>QDateTimeEdit</extends>
<header>qgsdatetimeedit.h</header> <header>qgsdatetimeedit.h</header>
</customwidget> </customwidget>
<customwidget>
<class>QgsExtentGroupBox</class>
<extends>QgsCollapsibleGroupBox</extends>
<header>qgsextentgroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>QgsFontButton</class> <class>QgsFontButton</class>
<extends>QToolButton</extends> <extends>QToolButton</extends>
@ -3699,7 +3693,6 @@
<tabstop>pbnRemoveScale</tabstop> <tabstop>pbnRemoveScale</tabstop>
<tabstop>pbnImportScales</tabstop> <tabstop>pbnImportScales</tabstop>
<tabstop>pbnExportScales</tabstop> <tabstop>pbnExportScales</tabstop>
<tabstop>mExtentGroupBox</tabstop>
<tabstop>scrollArea</tabstop> <tabstop>scrollArea</tabstop>
<tabstop>scrollArea_3</tabstop> <tabstop>scrollArea_3</tabstop>
<tabstop>mShowDatumTransformDialogCheckBox</tabstop> <tabstop>mShowDatumTransformDialogCheckBox</tabstop>