From 885c0b6136ba0f5fdb5179ab10f0b5d4bbd6372b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 9 Mar 2023 13:52:59 +1000 Subject: [PATCH] Add a "refreshing" icon overlay for layout items which are being redrawn/updated in the background Gives immediate visual feedback to users that the current appearance of those items are outdated and to wait while they update. --- images/composer/refreshing_item.svg | 1 + images/images.qrc | 1 + .../layout/qgslayoutitem.sip.in | 14 +++++++++++ src/core/layout/qgslayoutitem.cpp | 23 +++++++++++++++++++ src/core/layout/qgslayoutitem.h | 14 +++++++++++ src/core/layout/qgslayoutitemmap.cpp | 9 ++++++++ 6 files changed, 62 insertions(+) create mode 100644 images/composer/refreshing_item.svg 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 {