diff --git a/python/core/auto_generated/labeling/qgspallabeling.sip.in b/python/core/auto_generated/labeling/qgspallabeling.sip.in index 00a82f357b5..406c5ec260b 100644 --- a/python/core/auto_generated/labeling/qgspallabeling.sip.in +++ b/python/core/auto_generated/labeling/qgspallabeling.sip.in @@ -636,7 +636,7 @@ Sets the label placement ``settings``. .. versionadded:: 3.26 %End - static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 ); + static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() ); %Docstring Returns a pixmap preview for label ``settings``. @@ -644,7 +644,7 @@ Returns a pixmap preview for label ``settings``. :param size: target pixmap size :param previewText: text to render in preview, or empty for default text :param padding: space between icon edge and color ramp -:param devicePixelRatio: can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32) +:param screen: can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32) .. versionadded:: 3.10 %End diff --git a/python/core/auto_generated/qgsscreenproperties.sip.in b/python/core/auto_generated/qgsscreenproperties.sip.in index 0308dddf2dd..69f30b0e604 100644 --- a/python/core/auto_generated/qgsscreenproperties.sip.in +++ b/python/core/auto_generated/qgsscreenproperties.sip.in @@ -35,6 +35,10 @@ Constructor for QgsScreenProperties, copying properties from the specified ``screen``. %End + bool operator==( const QgsScreenProperties &other ) const; + + bool operator!=( const QgsScreenProperties &other ) const; + bool isValid() const; %Docstring Returns ``True`` if the properties are valid. @@ -70,6 +74,7 @@ to match the screen settings. }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/core/auto_generated/symbology/qgscombinedstylemodel.sip.in b/python/core/auto_generated/symbology/qgscombinedstylemodel.sip.in index 6351bd29248..ed8e849bc29 100644 --- a/python/core/auto_generated/symbology/qgscombinedstylemodel.sip.in +++ b/python/core/auto_generated/symbology/qgscombinedstylemodel.sip.in @@ -9,8 +9,6 @@ - - class QgsCombinedStyleModel: QConcatenateTablesProxyModel { %Docstring(signature="appended") @@ -79,12 +77,12 @@ This allows style icons to be generated at an icon size which corresponds exactly to the view's icon size in which this model is used. %End - void addDesiredIconDevicePixelRatio( double ratio ); + void addTargetScreenProperties( const QgsScreenProperties &properties ); %Docstring -Adds an additional icon device pixel ``ratio`` to generate for Qt.DecorationRole data. +Adds additional target screen ``properties`` to use when generating icons for Qt.DecorationRole data. -This allows style icons to be generated at an icon device pixel ratio which -corresponds exactly to the view's icon size in which this model is used. +This allows style icons to be generated at an icon device pixel ratio and DPI which +corresponds exactly to the view's screen properties in which this model is used. .. versionadded:: 3.32 %End diff --git a/python/core/auto_generated/symbology/qgsstylemodel.sip.in b/python/core/auto_generated/symbology/qgsstylemodel.sip.in index 6f4edae4da1..e09ad67a459 100644 --- a/python/core/auto_generated/symbology/qgsstylemodel.sip.in +++ b/python/core/auto_generated/symbology/qgsstylemodel.sip.in @@ -90,12 +90,12 @@ This allows style icons to be generated at an icon size which corresponds exactly to the view's icon size in which this model is used. %End - void addDesiredIconDevicePixelRatio( double ratio ); + void addTargetScreenProperties( const QgsScreenProperties &properties ); %Docstring -Adds an additional icon device pixel ``ratio`` to generate for Qt.DecorationRole data. +Adds additional target screen ``properties`` to use when generating icons for Qt.DecorationRole data. -This allows style icons to be generated at an icon device pixel ratio which -corresponds exactly to the view's icon size in which this model is used. +This allows style icons to be generated at an icon device pixel ratio and DPI which +corresponds exactly to the view's screen properties in which this model is used. .. versionadded:: 3.32 %End @@ -364,12 +364,12 @@ This allows style icons to be generated at an icon size which corresponds exactly to the view's icon size in which this model is used. %End - void addDesiredIconDevicePixelRatio( double ratio ); + void addTargetScreenProperties( const QgsScreenProperties &properties ); %Docstring -Adds an additional icon device pixel ``ratio`` to generate for Qt.DecorationRole data. +Adds additional target screen ``properties`` to use when generating icons for Qt.DecorationRole data. -This allows style icons to be generated at an icon device pixel ratio which -corresponds exactly to the view's icon size in which this model is used. +This allows style icons to be generated at an icon device pixel ratio and DPI which +corresponds exactly to the view's screen properties in which this model is used. .. versionadded:: 3.32 %End diff --git a/python/core/auto_generated/textrenderer/qgstextformat.sip.in b/python/core/auto_generated/textrenderer/qgstextformat.sip.in index a8add20c417..884909cfd4c 100644 --- a/python/core/auto_generated/textrenderer/qgstextformat.sip.in +++ b/python/core/auto_generated/textrenderer/qgstextformat.sip.in @@ -718,7 +718,7 @@ Updates the format by evaluating current values of data defined properties. .. versionadded:: 3.10 %End - static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 ); + static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() ); %Docstring Returns a pixmap preview for a text ``format``. @@ -726,7 +726,7 @@ Returns a pixmap preview for a text ``format``. :param size: target pixmap size :param previewText: text to render in preview, or empty for default text :param padding: space between icon edge and color ramp -:param devicePixelRatio: can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32) +:param screen: can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32) .. versionadded:: 3.10 %End diff --git a/src/core/labeling/qgspallabeling.cpp b/src/core/labeling/qgspallabeling.cpp index d2f04f2d3a7..56c594abdc4 100644 --- a/src/core/labeling/qgspallabeling.cpp +++ b/src/core/labeling/qgspallabeling.cpp @@ -1357,8 +1357,10 @@ void QgsPalLayerSettings::setCallout( QgsCallout *callout ) mCallout.reset( callout ); } -QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText, int padding, double devicePixelRatio ) +QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen ) { + const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1; + // for now, just use format QgsTextFormat tempFormat = settings.format(); QPixmap pixmap( size * devicePixelRatio ); @@ -1406,10 +1408,24 @@ QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettin context.setMapToPixel( newCoordXForm ); context.setFlag( Qgis::RenderContextFlag::Antialiasing, true ); - QWidget *activeWindow = QApplication::activeWindow(); - const double physicalDpiX = activeWindow && activeWindow->screen() ? activeWindow->screen()->physicalDotsPerInchX() : 96.0; - context.setScaleFactor( physicalDpiX / 25.4 ); - context.setDevicePixelRatio( devicePixelRatio ); + if ( screen.isValid() ) + { + screen.updateRenderContextForScreen( context ); + } + else + { + QWidget *activeWindow = QApplication::activeWindow(); + if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr ) + { + context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 ); + context.setDevicePixelRatio( screen->devicePixelRatio() ); + } + else + { + context.setScaleFactor( 96.0 / 25.4 ); + context.setDevicePixelRatio( 1.0 ); + } + } context.setUseAdvancedEffects( true ); context.setPainter( &painter ); diff --git a/src/core/labeling/qgspallabeling.h b/src/core/labeling/qgspallabeling.h index 5c9e6662c68..32cc504be57 100644 --- a/src/core/labeling/qgspallabeling.h +++ b/src/core/labeling/qgspallabeling.h @@ -968,10 +968,10 @@ class CORE_EXPORT QgsPalLayerSettings * \param size target pixmap size * \param previewText text to render in preview, or empty for default text * \param padding space between icon edge and color ramp - * \param devicePixelRatio can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32) + * \param screen can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32) * \since QGIS 3.10 */ - static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 ); + static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() ); /** * Returns the layer's unplaced label visibility. diff --git a/src/core/qgsscreenproperties.h b/src/core/qgsscreenproperties.h index 5f4c803bae0..b865e76c8ea 100644 --- a/src/core/qgsscreenproperties.h +++ b/src/core/qgsscreenproperties.h @@ -16,6 +16,8 @@ #define QGSSCREENPROPERTIES_H #include "qgis_core.h" +#include "qgis_sip.h" +#include "qgis.h" class QScreen; class QgsRenderContext; @@ -46,6 +48,18 @@ class CORE_EXPORT QgsScreenProperties */ explicit QgsScreenProperties( const QScreen *screen ); + bool operator==( const QgsScreenProperties &other ) const + { + return mValid == other.mValid + && mDevicePixelRatio == other.mDevicePixelRatio + && mPhysicalDpi == other.mPhysicalDpi; + } + + bool operator!=( const QgsScreenProperties &other ) const + { + return !( *this == other ); + } + /** * Returns TRUE if the properties are valid. */ @@ -84,4 +98,14 @@ class CORE_EXPORT QgsScreenProperties double mPhysicalDpi = 96; }; +/** + * Returns a hash for a screen \a properties. + * + * \since QGIS 3.32 + */ +CORE_EXPORT inline uint qHash( const QgsScreenProperties &properties ) SIP_SKIP +{ + return qHash( properties.devicePixelRatio() ) + qHash( properties.physicalDpi() ); +} + #endif // QGSSCREENPROPERTIES_H diff --git a/src/core/symbology/qgscombinedstylemodel.cpp b/src/core/symbology/qgscombinedstylemodel.cpp index 45633844f24..06c5efdb135 100644 --- a/src/core/symbology/qgscombinedstylemodel.cpp +++ b/src/core/symbology/qgscombinedstylemodel.cpp @@ -76,9 +76,9 @@ void QgsCombinedStyleModel::addStyle( QgsStyle *style ) styleModel->addDesiredIconSize( size ); } - for ( double ratio : std::as_const( mDevicePixelRatios ) ) + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) { - styleModel->addDesiredIconDevicePixelRatio( ratio ); + styleModel->addTargetScreenProperties( *it ); } addSourceModel( styleModel ); @@ -135,9 +135,9 @@ void QgsCombinedStyleModel::addDefaultStyle() styleModel->addDesiredIconSize( size ); } - for ( double ratio : std::as_const( mDevicePixelRatios ) ) + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) { - styleModel->addDesiredIconDevicePixelRatio( ratio ); + styleModel->addTargetScreenProperties( *it ); } addSourceModel( styleModel ); @@ -164,18 +164,18 @@ void QgsCombinedStyleModel::addDesiredIconSize( QSize size ) } } -void QgsCombinedStyleModel::addDesiredIconDevicePixelRatio( double ratio ) +void QgsCombinedStyleModel::addTargetScreenProperties( const QgsScreenProperties &properties ) { - if ( !mDevicePixelRatios.contains( ratio ) ) - mDevicePixelRatios.append( ratio ); + if ( !mTargetScreenProperties.contains( properties ) ) + mTargetScreenProperties.insert( properties ); for ( auto it = mOwnedStyleModels.constBegin(); it != mOwnedStyleModels.constEnd(); ++it ) { - it.value()->addDesiredIconDevicePixelRatio( ratio ); + it.value()->addTargetScreenProperties( properties ); } if ( mStyles.contains( QgsStyle::defaultStyle() ) ) { - QgsApplication::defaultStyleModel()->addDesiredIconDevicePixelRatio( ratio ); + QgsApplication::defaultStyleModel()->addTargetScreenProperties( properties ); } } diff --git a/src/core/symbology/qgscombinedstylemodel.h b/src/core/symbology/qgscombinedstylemodel.h index db9eb7130b8..d9e99eb7b0c 100644 --- a/src/core/symbology/qgscombinedstylemodel.h +++ b/src/core/symbology/qgscombinedstylemodel.h @@ -19,13 +19,13 @@ #include "qgis_core.h" #include "qgis_sip.h" #include - +#include #include class QgsStyle; class QgsStyleModel; class QgsSingleItemModel; - +class QgsScreenProperties; /** * \ingroup core @@ -92,14 +92,14 @@ class CORE_EXPORT QgsCombinedStyleModel: public QConcatenateTablesProxyModel void addDesiredIconSize( QSize size ); /** - * Adds an additional icon device pixel \a ratio to generate for Qt::DecorationRole data. + * Adds additional target screen \a properties to use when generating icons for Qt::DecorationRole data. * - * This allows style icons to be generated at an icon device pixel ratio which - * corresponds exactly to the view's icon size in which this model is used. + * This allows style icons to be generated at an icon device pixel ratio and DPI which + * corresponds exactly to the view's screen properties in which this model is used. * * \since QGIS 3.32 */ - void addDesiredIconDevicePixelRatio( double ratio ); + void addTargetScreenProperties( const QgsScreenProperties &properties ); private: @@ -108,7 +108,7 @@ class CORE_EXPORT QgsCombinedStyleModel: public QConcatenateTablesProxyModel QHash< QgsStyle *, QgsSingleItemModel * > mTitleModels; QList< QSize > mAdditionalSizes; - QList< double > mDevicePixelRatios; + QSet< QgsScreenProperties > mTargetScreenProperties; }; diff --git a/src/core/symbology/qgsstylemodel.cpp b/src/core/symbology/qgsstylemodel.cpp index 9d2786d2c3c..3d088cc7041 100644 --- a/src/core/symbology/qgsstylemodel.cpp +++ b/src/core/symbology/qgsstylemodel.cpp @@ -39,13 +39,6 @@ QgsAbstractStyleEntityIconGenerator *QgsStyleModel::sIconGenerator = nullptr; QgsAbstractStyleEntityIconGenerator::QgsAbstractStyleEntityIconGenerator( QObject *parent ) : QObject( parent ) { - // Default to generating icons using the application devicePixelRatio. - // Additional ratios may be added by individual views to this model (eg on - // different screens with different pixel ratios) - if ( QGuiApplication *guiApplication = qobject_cast< QGuiApplication * >( QApplication::instance() ) ) - mDevicePixelRatios.append( guiApplication->devicePixelRatio() ); - else - mDevicePixelRatios.append( 1.0 ); } void QgsAbstractStyleEntityIconGenerator::setIconSizes( const QList &sizes ) @@ -58,14 +51,14 @@ QList QgsAbstractStyleEntityIconGenerator::iconSizes() const return mIconSizes; } -void QgsAbstractStyleEntityIconGenerator::setDevicePixelRatios( const QList &ratios ) +void QgsAbstractStyleEntityIconGenerator::setTargetScreenProperties( const QSet &properties ) { - mDevicePixelRatios = ratios; + mTargetScreenProperties = properties; } -QList QgsAbstractStyleEntityIconGenerator::devicePixelRatios() const +QSet QgsAbstractStyleEntityIconGenerator::targetScreenProperties() const { - return mDevicePixelRatios; + return mTargetScreenProperties; } @@ -79,19 +72,15 @@ QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent ) { Q_ASSERT( mStyle ); - // Default to generating icons using the application devicePixelRatio. - // Additional ratios may be added by individual views to this model (eg on - // different screens with different pixel ratios) - if ( QGuiApplication *guiApplication = qobject_cast< QGuiApplication * >( QApplication::instance() ) ) - mDevicePixelRatios.append( guiApplication->devicePixelRatio() ); - else - mDevicePixelRatios.append( 1.0 ); - for ( QgsStyle::StyleEntity entity : ENTITIES ) { mEntityNames.insert( entity, mStyle->allNames( entity ) ); } + // ensure we always generate icons using default screen properties + // in addition to actual target screen properties (ie device pixel ratio of 1, 96 dpi) + mTargetScreenProperties.insert( QgsScreenProperties() ); + connect( mStyle, &QgsStyle::entityAdded, this, &QgsStyleModel::onEntityAdded ); connect( mStyle, &QgsStyle::entityRemoved, this, &QgsStyleModel::onEntityRemoved ); connect( mStyle, &QgsStyle::entityRenamed, this, &QgsStyleModel::onEntityRename ); @@ -149,6 +138,14 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const QString tooltip = QStringLiteral( "

%1

%2" ).arg( name, tags.count() > 0 ? tags.join( QLatin1String( ", " ) ) : tr( "Not tagged" ) ); + // generate tooltips targeting the largest device pixel ratio for all attached screens + QgsScreenProperties maxDevicePixelRatioScreen; + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) + { + if ( !maxDevicePixelRatioScreen.isValid() || it->devicePixelRatio() > maxDevicePixelRatioScreen.devicePixelRatio() ) + maxDevicePixelRatioScreen = *it; + } + switch ( entityType ) { case QgsStyle::SymbolEntity: @@ -159,11 +156,11 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const { int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 ); int height = static_cast< int >( width / 1.61803398875 ); // golden ratio - QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get() ); + QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get(), nullptr, maxDevicePixelRatioScreen ); QByteArray data; QBuffer buffer( &data ); pm.save( &buffer, "PNG", 100 ); - tooltip += QStringLiteral( "

" ).arg( QString( data.toBase64() ) ); + tooltip += QStringLiteral( "

" ).arg( QString( data.toBase64() ) ).arg( width ); } break; } @@ -173,8 +170,7 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 ); int height = static_cast< int >( width / 1.61803398875 ); // golden ratio const QgsTextFormat format = mStyle->textFormat( name ); - const double maxDevicePixelRatio = *std::max_element( mDevicePixelRatios.constBegin(), mDevicePixelRatios.constEnd() ); - QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20, maxDevicePixelRatio ); + QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen ); QByteArray data; QBuffer buffer( &data ); pm.save( &buffer, "PNG", 100 ); @@ -186,9 +182,8 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const { int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 ); int height = static_cast< int >( width / 1.61803398875 ); // golden ratio - const double maxDevicePixelRatio = *std::max_element( mDevicePixelRatios.constBegin(), mDevicePixelRatios.constEnd() ); const QgsPalLayerSettings settings = mStyle->labelSettings( name ); - QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20, maxDevicePixelRatio ); + QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen ); QByteArray data; QBuffer buffer( &data ); pm.save( &buffer, "PNG", 100 ); @@ -204,11 +199,11 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const const QgsLegendPatchShape shape = mStyle->legendPatchShape( name ); if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) ) { - QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape ); + QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape, maxDevicePixelRatioScreen ); QByteArray data; QBuffer buffer( &data ); pm.save( &buffer, "PNG", 100 ); - tooltip += QStringLiteral( "

" ).arg( QString( data.toBase64() ) ); + tooltip += QStringLiteral( "

" ).arg( QString( data.toBase64() ) ).arg( width ); } break; } @@ -262,12 +257,15 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) ); if ( symbol ) { - if ( mAdditionalSizes.isEmpty() ) - icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get() ) ); - - for ( const QSize &s : mAdditionalSizes ) + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) { - icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get() ) ); + if ( mAdditionalSizes.isEmpty() ) + icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), nullptr, *it ) ); + + for ( const QSize &s : mAdditionalSizes ) + { + icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), nullptr, *it ) ); + } } } @@ -304,13 +302,13 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const return icon; const QgsTextFormat format( mStyle->textFormat( name ) ); - for ( const double pixelRatio : std::as_const( mDevicePixelRatios ) ) + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) { if ( mAdditionalSizes.isEmpty() ) - icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1, pixelRatio ) ); + icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1, *it ) ); for ( const QSize &s : mAdditionalSizes ) { - icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), pixelRatio ) ); + icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) ); } } mIconCache[ entityType ].insert( name, icon ); @@ -325,13 +323,13 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const return icon; const QgsPalLayerSettings settings( mStyle->labelSettings( name ) ); - for ( const double pixelRatio : std::as_const( mDevicePixelRatios ) ) + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) { if ( mAdditionalSizes.isEmpty() ) - icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1, pixelRatio ) ); + icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1, *it ) ); for ( const QSize &s : mAdditionalSizes ) { - icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), pixelRatio ) ); + icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) ); } } mIconCache[ entityType ].insert( name, icon ); @@ -348,14 +346,17 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const const QgsLegendPatchShape shape = mStyle->legendPatchShape( name ); if ( !shape.isNull() ) { - if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) ) + for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it ) { - if ( mAdditionalSizes.isEmpty() ) - icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape ) ); - - for ( const QSize &s : mAdditionalSizes ) + if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) ) { - icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape ) ); + if ( mAdditionalSizes.isEmpty() ) + icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape, *it ) ); + + for ( const QSize &s : mAdditionalSizes ) + { + icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape, *it ) ); + } } } } @@ -628,15 +629,15 @@ void QgsStyleModel::addDesiredIconSize( QSize size ) mIconCache.clear(); } -void QgsStyleModel::addDesiredIconDevicePixelRatio( double ratio ) +void QgsStyleModel::addTargetScreenProperties( const QgsScreenProperties &properties ) { - if ( mDevicePixelRatios.contains( ratio ) ) + if ( mTargetScreenProperties.contains( properties ) ) return; - mDevicePixelRatios << ratio; + mTargetScreenProperties.insert( properties ); if ( sIconGenerator ) - sIconGenerator->setDevicePixelRatios( mDevicePixelRatios ); + sIconGenerator->setTargetScreenProperties( mTargetScreenProperties ); mIconCache.clear(); } @@ -995,12 +996,12 @@ void QgsStyleProxyModel::addDesiredIconSize( QSize size ) mCombinedModel->addDesiredIconSize( size ); } -void QgsStyleProxyModel::addDesiredIconDevicePixelRatio( double ratio ) +void QgsStyleProxyModel::addTargetScreenProperties( const QgsScreenProperties &properties ) { if ( mModel ) - mModel->addDesiredIconDevicePixelRatio( ratio ); + mModel->addTargetScreenProperties( properties ); if ( mCombinedModel ) - mCombinedModel->addDesiredIconDevicePixelRatio( ratio ); + mCombinedModel->addTargetScreenProperties( properties ); } bool QgsStyleProxyModel::symbolTypeFilterEnabled() const diff --git a/src/core/symbology/qgsstylemodel.h b/src/core/symbology/qgsstylemodel.h index 28941b00758..4141c74e7a7 100644 --- a/src/core/symbology/qgsstylemodel.h +++ b/src/core/symbology/qgsstylemodel.h @@ -74,20 +74,26 @@ class CORE_EXPORT QgsAbstractStyleEntityIconGenerator : public QObject QList< QSize > iconSizes() const; /** - * Sets the list of icon device pixel \a ratios to generate. + * Sets the target screen \a properties to use when generating icons. * - * \see devicePixelRatios() + * This allows style icons to be generated at an icon device pixel ratio and DPI which + * corresponds exactly to the view's screen properties in which this model is used. + * + * \see targetScreenProperties() * \since QGIS 3.32 */ - void setDevicePixelRatios( const QList< double > &ratios ); + void setTargetScreenProperties( const QSet< QgsScreenProperties > &properties ); /** - * Returns the list of icon device pixel ratios to generate. + * Returns the target screen properties to use when generating icons. * - * \see setDevicePixelRatios() + * This allows style icons to be generated at an icon device pixel ratio and DPI which + * corresponds exactly to the view's screen properties in which this model is used. + * + * \see setTargetScreenProperties() * \since QGIS 3.32 */ - QList< double > devicePixelRatios() const; + QSet< QgsScreenProperties > targetScreenProperties() const; signals: @@ -100,7 +106,7 @@ class CORE_EXPORT QgsAbstractStyleEntityIconGenerator : public QObject private: QList< QSize > mIconSizes; - QList< double > mDevicePixelRatios; + QSet< QgsScreenProperties > mTargetScreenProperties; }; @@ -183,14 +189,14 @@ class CORE_EXPORT QgsStyleModel: public QAbstractItemModel void addDesiredIconSize( QSize size ); /** - * Adds an additional icon device pixel \a ratio to generate for Qt::DecorationRole data. + * Adds additional target screen \a properties to use when generating icons for Qt::DecorationRole data. * - * This allows style icons to be generated at an icon device pixel ratio which - * corresponds exactly to the view's icon size in which this model is used. + * This allows style icons to be generated at an icon device pixel ratio and DPI which + * corresponds exactly to the view's screen properties in which this model is used. * * \since QGIS 3.32 */ - void addDesiredIconDevicePixelRatio( double ratio ); + void addTargetScreenProperties( const QgsScreenProperties &properties ); /** * Sets the icon \a generator to use for deferred style entity icon generation. @@ -219,7 +225,7 @@ class CORE_EXPORT QgsStyleModel: public QAbstractItemModel QHash< QgsStyle::StyleEntity, QStringList > mEntityNames; - QList< double > mDevicePixelRatios; + QSet< QgsScreenProperties > mTargetScreenProperties; QList< QSize > mAdditionalSizes; mutable std::unique_ptr< QgsExpressionContext > mExpressionContext; @@ -483,14 +489,14 @@ class CORE_EXPORT QgsStyleProxyModel: public QSortFilterProxyModel void addDesiredIconSize( QSize size ); /** - * Adds an additional icon device pixel \a ratio to generate for Qt::DecorationRole data. + * Adds additional target screen \a properties to use when generating icons for Qt::DecorationRole data. * - * This allows style icons to be generated at an icon device pixel ratio which - * corresponds exactly to the view's icon size in which this model is used. + * This allows style icons to be generated at an icon device pixel ratio and DPI which + * corresponds exactly to the view's screen properties in which this model is used. * * \since QGIS 3.32 */ - void addDesiredIconDevicePixelRatio( double ratio ); + void addTargetScreenProperties( const QgsScreenProperties &properties ); public slots: diff --git a/src/core/textrenderer/qgstextformat.cpp b/src/core/textrenderer/qgstextformat.cpp index 864e5dca00e..2074c3b12a9 100644 --- a/src/core/textrenderer/qgstextformat.cpp +++ b/src/core/textrenderer/qgstextformat.cpp @@ -1104,8 +1104,9 @@ void QgsTextFormat::updateDataDefinedProperties( QgsRenderContext &context ) mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties ); } -QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, double devicePixelRatio ) +QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen ) { + const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1; QgsTextFormat tempFormat = format; QPixmap pixmap( size * devicePixelRatio ); pixmap.fill( Qt::transparent ); @@ -1152,10 +1153,24 @@ QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSi newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 ); context.setMapToPixel( newCoordXForm ); - QWidget *activeWindow = QApplication::activeWindow(); - const double physicalDpiX = ( activeWindow && activeWindow->screen() ? activeWindow->screen()->physicalDotsPerInch() : 96.0 ); - context.setScaleFactor( physicalDpiX / 25.4 ); - context.setDevicePixelRatio( devicePixelRatio ); + if ( screen.isValid() ) + { + screen.updateRenderContextForScreen( context ); + } + else + { + QWidget *activeWindow = QApplication::activeWindow(); + if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr ) + { + context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 ); + context.setDevicePixelRatio( screen->devicePixelRatio() ); + } + else + { + context.setScaleFactor( 96.0 / 25.4 ); + context.setDevicePixelRatio( 1.0 ); + } + } context.setUseAdvancedEffects( true ); context.setFlag( Qgis::RenderContextFlag::Antialiasing, true ); diff --git a/src/core/textrenderer/qgstextformat.h b/src/core/textrenderer/qgstextformat.h index 87453b55e7b..4d699e77180 100644 --- a/src/core/textrenderer/qgstextformat.h +++ b/src/core/textrenderer/qgstextformat.h @@ -24,6 +24,7 @@ #include "qgstextshadowsettings.h" #include "qgstextmasksettings.h" #include "qgsstringutils.h" +#include "qgsscreenproperties.h" #include @@ -651,10 +652,10 @@ class CORE_EXPORT QgsTextFormat * \param size target pixmap size * \param previewText text to render in preview, or empty for default text * \param padding space between icon edge and color ramp - * \param devicePixelRatio can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32) + * \param screen can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32) * \since QGIS 3.10 */ - static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 ); + static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() ); private: diff --git a/src/gui/qgsstyleitemslistwidget.cpp b/src/gui/qgsstyleitemslistwidget.cpp index 26b9108fd4d..dd8c15eb11b 100644 --- a/src/gui/qgsstyleitemslistwidget.cpp +++ b/src/gui/qgsstyleitemslistwidget.cpp @@ -16,12 +16,10 @@ #include "qgsstyleitemslistwidget.h" #include "qgsstylemanagerdialog.h" -#include "qgsstylesavedialog.h" #include "qgspanelwidget.h" #include "qgssettings.h" #include "qgsgui.h" #include "qgswindowmanagerinterface.h" -#include "qgsapplication.h" #include "qgsproject.h" #include "qgsprojectstylesettings.h" #include @@ -253,7 +251,7 @@ void QgsStyleItemsListWidget::setStyle( QgsStyle *style ) mModel->addDesiredIconSize( viewSymbols->iconSize() ); mModel->addDesiredIconSize( mSymbolTreeView->iconSize() ); - mModel->addDesiredIconDevicePixelRatio( mSymbolTreeView->devicePixelRatioF() ); + mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) ); viewSymbols->setTextElideMode( Qt::TextElideMode::ElideRight ); diff --git a/src/gui/symbology/qgsstyleexportimportdialog.cpp b/src/gui/symbology/qgsstyleexportimportdialog.cpp index 2d8502e1fcb..2b3d92cdc8d 100644 --- a/src/gui/symbology/qgsstyleexportimportdialog.cpp +++ b/src/gui/symbology/qgsstyleexportimportdialog.cpp @@ -20,9 +20,6 @@ #include "qgsapplication.h" #include "qgsstyle.h" #include "qgssymbol.h" -#include "qgssymbollayerutils.h" -#include "qgscolorramp.h" -#include "qgslogger.h" #include "qgsnetworkcontentfetchertask.h" #include "qgsstylegroupselectiondialog.h" #include "qgsguiutils.h" @@ -128,7 +125,7 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle *style, QWidget mModel = new QgsStyleProxyModel( dialogStyle, this ); mModel->addDesiredIconSize( listItems->iconSize() ); - mModel->addDesiredIconDevicePixelRatio( listItems->devicePixelRatioF() ); + mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) ); listItems->setModel( mModel ); diff --git a/src/gui/symbology/qgsstylemanagerdialog.cpp b/src/gui/symbology/qgsstylemanagerdialog.cpp index 35304471a8b..4d14e524108 100644 --- a/src/gui/symbology/qgsstylemanagerdialog.cpp +++ b/src/gui/symbology/qgsstylemanagerdialog.cpp @@ -502,7 +502,7 @@ void QgsStyleManagerDialog::setCurrentStyle( QgsStyle *style ) } mModel->addDesiredIconSize( mSymbolTreeView->iconSize() ); mModel->addDesiredIconSize( listItems->iconSize() ); - mModel->addDesiredIconDevicePixelRatio( listItems->devicePixelRatioF() ); + mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) ); mModel->setFilterString( searchBox->text() );