diff --git a/images/composer/refreshing_item.svg b/images/composer/refreshing_item.svg
new file mode 100644
index 00000000000..f93d03ee953
--- /dev/null
+++ b/images/composer/refreshing_item.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/images.qrc b/images/images.qrc
index 111b8870ee9..9805a8254fa 100644
--- a/images/images.qrc
+++ b/images/images.qrc
@@ -979,6 +979,7 @@
themes/default/mIconFieldGeometry.svg
themes/default/algorithms/mAlgorithmRectanglesOvalsDiamonds.svg
themes/default/algorithms/mAlgorithmOffsetLines.svg
+ composer/refreshing_item.svg
qgis_tips/symbol_levels.png
diff --git a/python/core/auto_generated/layout/qgslayoutitem.sip.in b/python/core/auto_generated/layout/qgslayoutitem.sip.in
index ea8f510d1c0..564a24f270a 100644
--- a/python/core/auto_generated/layout/qgslayoutitem.sip.in
+++ b/python/core/auto_generated/layout/qgslayoutitem.sip.in
@@ -1072,6 +1072,13 @@ Returns the clipping path generated by this item, in layout coordinates.
indicates if a particular item can function as a clipping path provider.
.. versionadded:: 3.16
+%End
+
+ virtual bool isRefreshing() const;
+%Docstring
+Returns ``True`` if the item is currently refreshing content in the background.
+
+.. versionadded:: 3.32
%End
public slots:
@@ -1205,6 +1212,13 @@ Draws the frame around the item.
Draws the background for the item.
.. seealso:: :py:func:`framePath`
+%End
+
+ void drawRefreshingOverlay( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle );
+%Docstring
+Draws a "refreshing" overlay icon on the item.
+
+.. versionadded:: 3.2
%End
virtual void setFixedSize( const QgsLayoutSize &size );
diff --git a/src/core/layout/qgslayoutitem.cpp b/src/core/layout/qgslayoutitem.cpp
index c88b795e4db..b1ba0892afd 100644
--- a/src/core/layout/qgslayoutitem.cpp
+++ b/src/core/layout/qgslayoutitem.cpp
@@ -30,6 +30,7 @@
#include "qgsimageoperation.h"
#include "qgsexpressioncontextutils.h"
#include "qgslayoutrendercontext.h"
+#include "qgssvgcache.h"
#include
#include
@@ -416,6 +417,11 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it
painter->scale( context.scaleFactor(), context.scaleFactor() );
drawFrame( context );
}
+
+ if ( isRefreshing() && previewRender )
+ {
+ drawRefreshingOverlay( painter, itemStyle );
+ }
}
void QgsLayoutItem::setReferencePoint( const QgsLayoutItem::ReferencePoint point )
@@ -1162,6 +1168,11 @@ void QgsLayoutItem::rotateItem( const double angle, const QPointF transformOrigi
refreshItemRotation( &itemTransformOrigin );
}
+bool QgsLayoutItem::isRefreshing() const
+{
+ return false;
+}
+
QgsExpressionContext QgsLayoutItem::createExpressionContext() const
{
QgsExpressionContext context = QgsLayoutObject::createExpressionContext();
@@ -1255,6 +1266,18 @@ void QgsLayoutItem::drawBackground( QgsRenderContext &context )
p->drawPath( framePath() );
}
+void QgsLayoutItem::drawRefreshingOverlay( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle )
+{
+ const QgsScopedQPainterState painterState( painter );
+ bool fitsInCache = false;
+ const int xSize = QFontMetrics( QFont() ).horizontalAdvance( 'X' );
+ const QImage refreshingImage = QgsApplication::svgCache()->svgAsImage( QStringLiteral( ":/images/composer/refreshing_item.svg" ), xSize * 3, QColor(), QColor(), 1, 1, fitsInCache );
+
+ const double previewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
+ painter->scale( 1.0 / previewScaleFactor, 1.0 / previewScaleFactor );
+ painter->drawImage( xSize, xSize, refreshingImage );
+}
+
void QgsLayoutItem::setFixedSize( const QgsLayoutSize &size )
{
mFixedSize = size;
diff --git a/src/core/layout/qgslayoutitem.h b/src/core/layout/qgslayoutitem.h
index c8b841b9330..c203f73786d 100644
--- a/src/core/layout/qgslayoutitem.h
+++ b/src/core/layout/qgslayoutitem.h
@@ -1012,6 +1012,13 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual QgsGeometry clipPath() const;
+ /**
+ * Returns TRUE if the item is currently refreshing content in the background.
+ *
+ * \since QGIS 3.32
+ */
+ virtual bool isRefreshing() const;
+
public slots:
/**
@@ -1136,6 +1143,13 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual void drawBackground( QgsRenderContext &context );
+ /**
+ * Draws a "refreshing" overlay icon on the item.
+ *
+ * \since QGIS 3.2
+ */
+ void drawRefreshingOverlay( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle );
+
/**
* Sets a fixed \a size for the layout item, which prevents it from being freely
* resized. Set an empty size if item can be freely resized.
diff --git a/src/core/layout/qgslayoutitemmap.cpp b/src/core/layout/qgslayoutitemmap.cpp
index c919c305b50..430bdad1a7d 100644
--- a/src/core/layout/qgslayoutitemmap.cpp
+++ b/src/core/layout/qgslayoutitemmap.cpp
@@ -908,6 +908,8 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
if ( mLayout->renderContext().isPreviewRender() )
{
+ bool renderInProgress = false;
+
QgsScopedQPainterState painterState( painter );
painter->setClipRect( thisPaintRect );
if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
@@ -933,6 +935,7 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style, painter );
mBackgroundUpdateTimer->start( 1 );
}
+ renderInProgress = true;
}
else
{
@@ -941,6 +944,7 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
// cache was invalidated - trigger a background update
mPreviewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( style, painter );
mBackgroundUpdateTimer->start( 1 );
+ renderInProgress = true;
}
//Background color is already included in cached image, so no need to draw
@@ -963,6 +967,11 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
mGridStack->drawItems( painter );
drawAnnotations( painter );
drawMapFrame( painter );
+
+ if ( renderInProgress )
+ {
+ drawRefreshingOverlay( painter, style );
+ }
}
else
{