Skip to content

Commit

Permalink
Add Qgis::SymbolLayerUserFlags for user-controlled flags which
Browse files Browse the repository at this point in the history
alter the handling of symbol layers

These differ from Qgis::SymbolLayerFlag in that
Qgis::SymbolLayerFlag flags are used to reflect the inbuilt properties
of a symbol layer type, whereas Qgis::SymbolLayerUserFlag are optional,
user controlled flags which can be toggled for a symbol layer.

Add a flag `DisableSelectionRecoloring` which can be set for
symbol layers which prevents the layer from being recolored to
the render context selection color even when the feature
being rendered is selected

This provides a mechanism for an individual symbol layer to
avoid the forced recolor of selected features.
  • Loading branch information
nyalldawson committed Jul 30, 2023
1 parent e4244ef commit 587b984
Show file tree
Hide file tree
Showing 17 changed files with 301 additions and 103 deletions.
9 changes: 8 additions & 1 deletion python/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,18 @@
SymbolPreviewFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ = "If present, indicates that features should never be clipped to the map extent during rendering"
Qgis.SymbolLayerFlag.__doc__ = "Flags controlling behavior of symbol layers\n\n.. versionadded:: 3.22\n\n" + '* ``DisableFeatureClipping``: ' + Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__
Qgis.SymbolLayerFlag.__doc__ = "Flags controlling behavior of symbol layers\n\n.. note::\n\n These differ from Qgis.SymbolLayerUserFlag in that Qgis.SymbolLayerFlag flags are used to reflect the inbuilt properties\n of a symbol layer type, whereas Qgis.SymbolLayerUserFlag are optional, user controlled flags which can be toggled\n for a symbol layer.\n\n.. versionadded:: 3.22\n\n" + '* ``DisableFeatureClipping``: ' + Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__
# --
Qgis.SymbolLayerFlag.baseClass = Qgis
Qgis.SymbolLayerFlags.baseClass = Qgis
SymbolLayerFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.SymbolLayerUserFlag.DisableSelectionRecoloring.__doc__ = "If present, indicates that the symbol layer should not be recolored when rendering selected features"
Qgis.SymbolLayerUserFlag.__doc__ = "User-specified flags controlling behavior of symbol layers.\n\n.. note::\n\n These differ from Qgis.SymbolLayerFlag in that Qgis.SymbolLayerFlag flags are used to reflect the inbuilt properties\n of a symbol layer type, whereas Qgis.SymbolLayerUserFlag are optional, user controlled flags which can be toggled\n for a symbol layer.\n\n.. versionadded:: 3.34\n\n" + '* ``DisableSelectionRecoloring``: ' + Qgis.SymbolLayerUserFlag.DisableSelectionRecoloring.__doc__
# --
Qgis.SymbolLayerUserFlag.baseClass = Qgis
Qgis.SymbolLayerUserFlags.baseClass = Qgis
SymbolLayerUserFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
QgsDataItem.Type = Qgis.BrowserItemType
# monkey patching scoped based enum
QgsDataItem.Collection = Qgis.BrowserItemType.Collection
Expand Down
10 changes: 10 additions & 0 deletions python/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ The development version
typedef QFlags<Qgis::SymbolLayerFlag> SymbolLayerFlags;


enum class SymbolLayerUserFlag
{
DisableSelectionRecoloring,
};

typedef QFlags<Qgis::SymbolLayerUserFlag> SymbolLayerUserFlags;


enum class BrowserItemType
{
Collection,
Expand Down Expand Up @@ -2241,6 +2249,8 @@ QFlags<Qgis::SymbolPreviewFlag> operator|(Qgis::SymbolPreviewFlag f1, QFlags<Qgi

QFlags<Qgis::SymbolLayerFlag> operator|(Qgis::SymbolLayerFlag f1, QFlags<Qgis::SymbolLayerFlag> f2);

QFlags<Qgis::SymbolLayerUserFlag> operator|(Qgis::SymbolLayerUserFlag f1, QFlags<Qgis::SymbolLayerUserFlag> f2);

QFlags<Qgis::BrowserItemCapability> operator|(Qgis::BrowserItemCapability f1, QFlags<Qgis::BrowserItemCapability> f2);

QFlags<Qgis::SublayerQueryFlag> operator|(Qgis::SublayerQueryFlag f1, QFlags<Qgis::SublayerQueryFlag> f2);
Expand Down
27 changes: 27 additions & 0 deletions python/core/auto_generated/symbology/qgssymbollayer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,24 @@ when desired.
.. seealso:: :py:func:`enabled`

.. versionadded:: 3.0
%End

Qgis::SymbolLayerUserFlags userFlags() const;
%Docstring
Returns user-controlled flags which control the symbol layer's behavior.

.. seealso:: :py:func:`setUserFlags`

.. versionadded:: 3.34
%End

void setUserFlags( Qgis::SymbolLayerUserFlags flags );
%Docstring
Sets user-controlled ``flags`` which control the symbol layer's behavior.

.. seealso:: :py:func:`userFlags`

.. versionadded:: 3.34
%End

virtual QColor color() const;
Expand Down Expand Up @@ -689,6 +707,7 @@ Constructor for QgsSymbolLayer.




void restoreOldDataDefinedProperties( const QVariantMap &stringMap );
%Docstring
Restores older data defined properties from string map.
Expand Down Expand Up @@ -734,6 +753,14 @@ if ``recursive`` is ``True`` masks are removed recursively for all children symb
.. seealso:: :py:func:`installMasks`

.. versionadded:: 3.30
%End

bool shouldRenderUsingSelectionColor( const QgsSymbolRenderContext &context ) const;
%Docstring
Returns ``True`` if the symbol layer should be rendered using the selection color
from the render context.

.. versionadded:: 3.34
%End

private:
Expand Down
28 changes: 28 additions & 0 deletions src/core/qgis.h
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,10 @@ class CORE_EXPORT Qgis
/**
* \brief Flags controlling behavior of symbol layers
*
* \note These differ from Qgis::SymbolLayerUserFlag in that Qgis::SymbolLayerFlag flags are used to reflect the inbuilt properties
* of a symbol layer type, whereas Qgis::SymbolLayerUserFlag are optional, user controlled flags which can be toggled
* for a symbol layer.
*
* \since QGIS 3.22
*/
enum class SymbolLayerFlag : int
Expand All @@ -543,6 +547,29 @@ class CORE_EXPORT Qgis
Q_DECLARE_FLAGS( SymbolLayerFlags, SymbolLayerFlag )
Q_FLAG( SymbolLayerFlags )

/**
* \brief User-specified flags controlling behavior of symbol layers.
*
* \note These differ from Qgis::SymbolLayerFlag in that Qgis::SymbolLayerFlag flags are used to reflect the inbuilt properties
* of a symbol layer type, whereas Qgis::SymbolLayerUserFlag are optional, user controlled flags which can be toggled
* for a symbol layer.
*
* \since QGIS 3.34
*/
enum class SymbolLayerUserFlag : int
{
DisableSelectionRecoloring = 1 << 0, //!< If present, indicates that the symbol layer should not be recolored when rendering selected features
};
Q_ENUM( SymbolLayerUserFlag )

/**
* Symbol layer user flags.
*
* \since QGIS 3.34
*/
Q_DECLARE_FLAGS( SymbolLayerUserFlags, SymbolLayerUserFlag )
Q_FLAG( SymbolLayerUserFlags )

/**
* Browser item types.
*
Expand Down Expand Up @@ -3895,6 +3922,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolRenderHints )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolPreviewFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolLayerFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolLayerUserFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::BrowserItemCapabilities )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SublayerQueryFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SublayerFlags )
Expand Down
13 changes: 7 additions & 6 deletions src/core/symbology/qgsarrowsymbollayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend
const double prevOpacity = mSymbol->opacity();
mSymbol->setOpacity( prevOpacity * context.opacity() );

const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
if ( isCurved() )
{
_resolveDataDefined( context );
Expand All @@ -763,7 +764,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend
const QPointF pd( points.back() );

const QPolygonF poly = curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
}
// straight arrow
else if ( points.size() == 2 )
Expand All @@ -774,7 +775,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend
const QPointF pd( points.at( 1 ) );

const QPolygonF poly = straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
}
}
else
Expand All @@ -797,7 +798,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend
const QPointF pd( points.at( pIdx + 2 ) );

const QPolygonF poly = curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
}
// straight arrow
else if ( points.size() - pIdx == 2 )
Expand All @@ -808,7 +809,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend
const QPointF pd( points.at( pIdx + 1 ) );

const QPolygonF poly = straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
}
}
}
Expand All @@ -827,7 +828,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend
const QPointF pd( points.back() );

const QPolygonF poly = straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
}
}
else
Expand All @@ -848,7 +849,7 @@ void QgsArrowSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRend

const QPolygonF poly = straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );

mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
mSymbol->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/core/symbology/qgsellipsesymbollayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,15 @@ void QgsEllipseSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &
transform.rotate( angle );
}

const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
if ( shapeIsFilled( shape ) )
{
p->setPen( context.selected() ? mSelPen : mPen );
p->setBrush( context.selected() ? mSelBrush : mBrush );
p->setPen( useSelectedColor ? mSelPen : mPen );
p->setBrush( useSelectedColor ? mSelBrush : mBrush );
}
else
{
p->setPen( context.selected() ? mSelPen : mPen );
p->setPen( useSelectedColor ? mSelPen : mPen );
p->setBrush( QBrush() );
}
p->drawPath( transform.map( mPainterPath ) );
Expand Down
39 changes: 25 additions & 14 deletions src/core/symbology/qgsfillsymbollayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,14 @@ void QgsSimpleFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVe
p->translate( offset );
}

const bool useSelectedColor = shouldRenderUsingSelectionColor( context );

#ifndef QT_NO_PRINTER
if ( mBrush.style() == Qt::SolidPattern || mBrush.style() == Qt::NoBrush || !dynamic_cast<QPrinter *>( p->device() ) )
#endif
{
p->setPen( context.selected() ? mSelPen : mPen );
p->setBrush( context.selected() ? mSelBrush : mBrush );
p->setPen( useSelectedColor ? mSelPen : mPen );
p->setBrush( useSelectedColor ? mSelBrush : mBrush );
_renderPolygon( p, points, rings, context );
}
#ifndef QT_NO_PRINTER
Expand All @@ -339,11 +341,11 @@ void QgsSimpleFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVe
// workaround upstream issue https://github.com/qgis/QGIS/issues/36580
// when a non-solid brush is set with opacity, the opacity incorrectly applies to the pen
// when exporting to PDF/print devices
p->setBrush( context.selected() ? mSelBrush : mBrush );
p->setBrush( useSelectedColor ? mSelBrush : mBrush );
p->setPen( Qt::NoPen );
_renderPolygon( p, points, rings, context );

p->setPen( context.selected() ? mSelPen : mPen );
p->setPen( useSelectedColor ? mSelPen : mPen );
p->setBrush( Qt::NoBrush );
_renderPolygon( p, points, rings, context );
}
Expand Down Expand Up @@ -988,7 +990,8 @@ void QgsGradientFillSymbolLayer::renderPolygon( const QPolygonF &points, const Q

applyDataDefinedSymbology( context, points );

p->setBrush( context.selected() ? mSelBrush : mBrush );
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
p->setBrush( useSelectedColor ? mSelBrush : mBrush );
p->setPen( Qt::NoPen );

QPointF offset = mOffset;
Expand Down Expand Up @@ -1297,7 +1300,8 @@ void QgsShapeburstFillSymbolLayer::renderPolygon( const QPolygonF &points, const
return;
}

if ( context.selected() )
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
if ( useSelectedColor )
{
//feature is selected, draw using selection style
p->setBrush( mSelBrush );
Expand Down Expand Up @@ -1805,7 +1809,8 @@ void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVec
mBrush.setTransform( t );
}

if ( context.selected() )
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
if ( useSelectedColor )
{
QColor selColor = context.renderContext().selectionColor();
p->setBrush( QBrush( selColor ) );
Expand Down Expand Up @@ -2174,12 +2179,13 @@ void QgsSVGFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVecto

if ( mStroke )
{
mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && useSelectedColor );
if ( rings )
{
for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
{
mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && useSelectedColor );
}
}
}
Expand Down Expand Up @@ -3107,9 +3113,10 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &
polygons.append( QPolygonF() << p5 << p6 );
}

const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
for ( const QPolygonF &polygon : std::as_const( polygons ) )
{
fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, useSelectedColor );
}

fillLineSymbol->stopRender( lineRenderContext );
Expand Down Expand Up @@ -3205,7 +3212,8 @@ void QgsLinePatternFillSymbolLayer::renderPolygon( const QPolygonF &points, cons

p->setPen( QPen( Qt::NoPen ) );

if ( context.selected() )
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
if ( useSelectedColor )
{
QColor selColor = context.renderContext().selectionColor();
p->setBrush( QBrush( selColor ) );
Expand Down Expand Up @@ -4042,7 +4050,8 @@ void QgsPointPatternFillSymbolLayer::renderPolygon( const QPolygonF &points, con

p->setPen( QPen( Qt::NoPen ) );

if ( context.selected() )
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
if ( useSelectedColor )
{
QColor selColor = context.renderContext().selectionColor();
p->setBrush( QBrush( selColor ) );
Expand Down Expand Up @@ -4788,7 +4797,8 @@ void QgsCentroidFillSymbolLayer::renderPolygon( const QPolygonF &points, const Q
// not rendering a feature, so we can just render the polygon immediately
const double prevOpacity = mMarker->opacity();
mMarker->setOpacity( mMarker->opacity() * context.opacity() );
render( context.renderContext(), QVector<Part>() << part, context.feature() ? *context.feature() : QgsFeature(), context.selected() );
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
render( context.renderContext(), QVector<Part>() << part, context.feature() ? *context.feature() : QgsFeature(), useSelectedColor );
mMarker->setOpacity( prevOpacity );
}
}
Expand Down Expand Up @@ -5471,7 +5481,8 @@ void QgsRandomMarkerFillSymbolLayer::renderPolygon( const QPolygonF &points, con
// not rendering a feature, so we can just render the polygon immediately
const double prevOpacity = mMarker->opacity();
mMarker->setOpacity( mMarker->opacity() * context.opacity() );
render( context.renderContext(), QVector< Part>() << part, context.feature() ? *context.feature() : QgsFeature(), context.selected() );
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
render( context.renderContext(), QVector< Part>() << part, context.feature() ? *context.feature() : QgsFeature(), useSelectedColor );
mMarker->setOpacity( prevOpacity );
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, Q
const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
context.renderContext().setFlag( Qgis::RenderContextFlag::RenderingSubSymbol );

mSymbol->renderFeature( f, context.renderContext(), -1, context.selected() );
const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
mSymbol->renderFeature( f, context.renderContext(), -1, useSelectedColor );

context.renderContext().setFlag( Qgis::RenderContextFlag::RenderingSubSymbol, prevIsSubsymbol );

Expand Down
Loading

0 comments on commit 587b984

Please sign in to comment.