33 : mLegendModel( legendModel )
34 , mSettings( settings )
40 return paintAndDetermineSize( renderContext );
45 paintAndDetermineSize( painter );
48 QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter )
50 return paintAndDetermineSizeInternal(
nullptr, painter );
53 QSizeF QgsLegendRenderer::paintAndDetermineSizeInternal(
QgsRenderContext *context, QPainter *painter )
60 QList<Atom> atomList = createAtomList( rootGroup, mSettings.
splitLayer() );
62 setColumns( atomList );
64 qreal maxColumnWidth = 0;
67 Q_FOREACH (
const Atom &atom, atomList )
69 maxColumnWidth = std::max( atom.size.width(), maxColumnWidth );
74 QSizeF titleSize = drawTitle();
76 titleSize.rwidth() += mSettings.
boxSpace() * 2.0;
79 QPointF point( mSettings.
boxSpace(), columnTop );
80 bool firstInColumn =
true;
81 double columnMaxHeight = 0;
82 qreal columnWidth = 0;
84 Q_FOREACH (
const Atom &atom, atomList )
86 if ( atom.column > column )
91 point.rx() += mSettings.
columnSpace() + maxColumnWidth;
95 point.rx() += mSettings.
columnSpace() + columnWidth;
97 point.ry() = columnTop;
100 firstInColumn =
true;
102 if ( !firstInColumn )
104 point.ry() += spaceAboveAtom( atom );
107 QSizeF atomSize = context ? drawAtom( atom, context, point )
108 : drawAtom( atom, painter, point );
109 columnWidth = std::max( atomSize.width(), columnWidth );
111 point.ry() += atom.size.height();
112 columnMaxHeight = std::max( point.y() - columnTop, columnMaxHeight );
114 firstInColumn =
false;
116 point.rx() += columnWidth + mSettings.
boxSpace();
118 size.rheight() = columnTop + columnMaxHeight + mSettings.
boxSpace();
119 size.rwidth() = point.x();
120 if ( !mSettings.
title().isEmpty() )
122 size.rwidth() = std::max( titleSize.width(), size.width() );
126 if ( mLegendSize.isValid() )
128 qreal w = std::max( size.width(), mLegendSize.width() );
129 qreal h = std::max( size.height(), mLegendSize.height() );
130 size = QSizeF( w, h );
134 if ( !mSettings.
title().isEmpty() )
142 point.rx() = size.width() / 2;
146 point.rx() = size.width() - mSettings.
boxSpace();
150 drawTitle( context, point, mSettings.
titleAlignment(), size.width() );
152 drawTitle( painter, point, mSettings.
titleAlignment(), size.width() );
159 QList<QgsLegendRenderer::Atom> QgsLegendRenderer::createAtomList(
QgsLayerTreeGroup *parentGroup,
bool splitLayer )
163 if ( !parentGroup )
return atoms;
172 QList<Atom> groupAtoms = createAtomList( nodeGroup, splitLayer );
173 bool hasSubItems = !groupAtoms.empty();
179 nucleon.size = drawGroupTitle( nodeGroup );
181 if ( !groupAtoms.isEmpty() )
184 groupAtoms[0].size.rheight() += spaceAboveAtom( groupAtoms[0] );
186 groupAtoms[0].nucleons.prepend( nucleon );
187 groupAtoms[0].size.rheight() += nucleon.size.height();
188 groupAtoms[0].size.rwidth() = std::max( nucleon.size.width(), groupAtoms[0].size.width() );
194 atom.nucleons.append( nucleon );
195 atom.size.rwidth() += nucleon.size.width();
196 atom.size.rheight() += nucleon.size.height();
197 atom.size.rwidth() = std::max( nucleon.size.width(), atom.size.width() );
198 groupAtoms.append( atom );
204 atoms.append( groupAtoms );
218 nucleon.size = drawLayerTitle( nodeLayer );
219 atom.nucleons.append( nucleon );
220 atom.size.rwidth() = nucleon.size.width();
221 atom.size.rheight() = nucleon.size.height();
224 QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->
layerLegendNodes( nodeLayer );
232 QList<Atom> layerAtoms;
234 for (
int j = 0; j < legendNodes.count(); j++ )
238 Nucleon symbolNucleon = drawSymbolItem( legendNode );
244 atom.size.rwidth() = std::max( symbolNucleon.size.width(), atom.size.width() );
246 if ( !atom.nucleons.isEmpty() )
251 atom.size.rheight() += symbolNucleon.size.height();
252 atom.nucleons.append( symbolNucleon );
257 symbolAtom.nucleons.append( symbolNucleon );
258 symbolAtom.size.rwidth() = symbolNucleon.size.width();
259 symbolAtom.size.rheight() = symbolNucleon.size.height();
260 layerAtoms.append( symbolAtom );
263 layerAtoms.prepend( atom );
264 atoms.append( layerAtoms );
272 void QgsLegendRenderer::setColumns( QList<Atom> &atomList )
277 double totalHeight = 0;
278 qreal maxAtomHeight = 0;
279 Q_FOREACH (
const Atom &atom, atomList )
281 totalHeight += spaceAboveAtom( atom );
282 totalHeight += atom.size.height();
283 maxAtomHeight = std::max( atom.size.height(), maxAtomHeight );
291 double maxColumnHeight = 0;
292 int currentColumn = 0;
293 int currentColumnAtomCount = 0;
294 double currentColumnHeight = 0;
295 double closedColumnsHeight = 0;
297 for (
int i = 0; i < atomList.size(); i++ )
300 double avgColumnHeight = ( totalHeight - closedColumnsHeight ) / ( mSettings.
columnCount() - currentColumn );
302 Atom atom = atomList.at( i );
303 double currentHeight = currentColumnHeight;
304 if ( currentColumnAtomCount > 0 )
305 currentHeight += spaceAboveAtom( atom );
306 currentHeight += atom.size.height();
308 bool canCreateNewColumn = ( currentColumnAtomCount > 0 )
309 && ( currentColumn < mSettings.
columnCount() - 1 );
311 bool shouldCreateNewColumn = ( currentHeight - avgColumnHeight ) > atom.size.height() / 2
312 && currentColumnAtomCount > 0
313 && currentHeight > maxAtomHeight
314 && currentHeight > maxColumnHeight;
318 shouldCreateNewColumn |= ( atomList.size() - i < mSettings.
columnCount() - currentColumn );
320 if ( canCreateNewColumn && shouldCreateNewColumn )
324 currentColumnAtomCount = 0;
325 closedColumnsHeight += currentColumnHeight;
326 currentColumnHeight = atom.size.height();
330 currentColumnHeight = currentHeight;
332 atomList[i].column = currentColumn;
333 currentColumnAtomCount++;
334 maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
338 QMap<QString, qreal> maxSymbolWidth;
339 for (
int i = 0; i < atomList.size(); i++ )
341 Atom &atom = atomList[i];
342 for (
int j = 0; j < atom.nucleons.size(); j++ )
346 QString key = QStringLiteral(
"%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column );
347 maxSymbolWidth[key] = std::max( atom.nucleons.at( j ).symbolSize.width(), maxSymbolWidth[key] );
351 for (
int i = 0; i < atomList.size(); i++ )
353 Atom &atom = atomList[i];
354 for (
int j = 0; j < atom.nucleons.size(); j++ )
358 QString key = QStringLiteral(
"%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column );
361 atom.nucleons[j].labelXOffset = maxSymbolWidth[key] + space;
362 atom.nucleons[j].size.rwidth() = maxSymbolWidth[key] + space + atom.nucleons.at( j ).labelSize.width();
368 QSizeF QgsLegendRenderer::drawTitle( QPainter *painter, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
370 return drawTitleInternal(
nullptr, painter, point, halignment, legendWidth );
373 QSizeF QgsLegendRenderer::drawTitleInternal(
QgsRenderContext *context, QPainter *painter, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
376 if ( mSettings.
title().isEmpty() )
382 double y = point.y();
384 if ( context && context->
painter() )
390 painter->setPen( mSettings.
fontColor() );
396 switch ( halignment )
398 case Qt::AlignHCenter:
399 textBoxWidth = ( std::min( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.
boxSpace() ) * 2.0;
400 textBoxLeft = point.x() - textBoxWidth / 2.;
404 textBoxWidth = point.x() - mSettings.
boxSpace();
408 textBoxLeft = point.x();
409 textBoxWidth = legendWidth - point.x() - mSettings.
boxSpace();
415 for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
422 QRectF r( textBoxLeft, y, textBoxWidth, height );
424 if ( context && context->
painter() )
426 mSettings.
drawText( context->
painter(), r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
430 mSettings.
drawText( painter, r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
434 size.rwidth() = std::max( width, size.rwidth() );
437 if ( titlePart != ( lines.end() - 1 ) )
442 size.rheight() = y - point.y();
448 double QgsLegendRenderer::spaceAboveAtom(
const Atom &atom )
450 if ( atom.nucleons.isEmpty() )
return 0;
452 Nucleon nucleon = atom.nucleons.first();
454 if (
QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
458 else if (
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
462 else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( nucleon.item ) )
473 QSizeF QgsLegendRenderer::drawAtom(
const Atom &atom, QPainter *painter, QPointF point )
475 return drawAtomInternal( atom,
nullptr, painter, point );
478 QSizeF QgsLegendRenderer::drawAtomInternal(
const Atom &atom,
QgsRenderContext *context, QPainter *painter, QPointF point )
481 QSizeF size = QSizeF( atom.size );
482 Q_FOREACH (
const Nucleon &nucleon, atom.nucleons )
484 if (
QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
494 drawGroupTitle( groupItem, context, point );
496 drawGroupTitle( groupItem, painter, point );
499 else if (
QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
509 drawLayerTitle( layerItem, context, point );
511 drawLayerTitle( layerItem, painter, point );
521 Nucleon symbolNucleon = context ? drawSymbolItem( legendNode, context, point, nucleon.labelXOffset )
522 : drawSymbolItem( legendNode, painter, point, nucleon.labelXOffset );
524 size.rwidth() = std::max( symbolNucleon.size.width(), size.width() );
526 point.ry() += nucleon.size.height();
532 QgsLegendRenderer::Nucleon QgsLegendRenderer::drawSymbolItem(
QgsLayerTreeModelLegendNode *symbolItem, QPainter *painter, QPointF point,
double labelXOffset )
534 return drawSymbolItemInternal( symbolItem,
nullptr, painter, point, labelXOffset );
555 : ( painter ? &ctx :
nullptr ) );
561 nucleon.item = symbolItem;
565 double width = std::max( static_cast< double >( im.
symbolSize.width() ), labelXOffset ) + im.
labelSize.width();
567 nucleon.size = QSizeF( width, height );
571 QSizeF QgsLegendRenderer::drawLayerTitle(
QgsLayerTreeLayer *nodeLayer, QPainter *painter, QPointF point )
573 return drawLayerTitleInternal( nodeLayer,
nullptr, painter, point );
579 QModelIndex idx = mLegendModel->
node2index( nodeLayer );
582 if ( mLegendModel->
data( idx, Qt::DisplayRole ).toString().isEmpty() )
585 double y = point.y();
587 if ( context && context->
painter() )
590 painter->setPen( mSettings.
fontColor() );
595 if ( context && nodeLayer->
layer() )
603 const QStringList lines = mSettings.
evaluateItemText( mLegendModel->
data( idx, Qt::DisplayRole ).toString(),
605 for ( QStringList::ConstIterator layerItemPart = lines.constBegin(); layerItemPart != lines.constEnd(); ++layerItemPart )
608 if ( context && context->
painter() )
609 mSettings.
drawText( context->
painter(), point.x(), y, *layerItemPart, layerFont );
611 mSettings.
drawText( painter, point.x(), y, *layerItemPart, layerFont );
613 size.rwidth() = std::max( width, size.width() );
614 if ( layerItemPart != ( lines.end() - 1 ) )
619 size.rheight() = y - point.y();
628 QSizeF QgsLegendRenderer::drawGroupTitle(
QgsLayerTreeGroup *nodeGroup, QPainter *painter, QPointF point )
630 return drawGroupTitleInternal( nodeGroup,
nullptr, painter, point );
636 QModelIndex idx = mLegendModel->
node2index( nodeGroup );
638 double y = point.y();
640 if ( context && context->
painter() )
643 painter->setPen( mSettings.
fontColor() );
649 const QStringList lines = mSettings.
evaluateItemText( mLegendModel->
data( idx, Qt::DisplayRole ).toString(),
651 for ( QStringList::ConstIterator groupPart = lines.constBegin(); groupPart != lines.constEnd(); ++groupPart )
654 if ( context && context->
painter() )
655 mSettings.
drawText( context->
painter(), point.x(), y, *groupPart, groupFont );
657 mSettings.
drawText( painter, point.x(), y, *groupPart, groupFont );
659 size.rwidth() = std::max( width, size.width() );
660 if ( groupPart != ( lines.end() - 1 ) )
665 size.rheight() = y - point.y();
671 QString style = node->
customProperty( QStringLiteral(
"legend/title-style" ) ).toString();
672 if ( style == QLatin1String(
"hidden" ) )
674 else if ( style == QLatin1String(
"group" ) )
676 else if ( style == QLatin1String(
"subgroup" ) )
703 str = QStringLiteral(
"hidden" );
706 str = QStringLiteral(
"group" );
709 str = QStringLiteral(
"subgroup" );
715 if ( !str.isEmpty() )
721 QSizeF QgsLegendRenderer::drawTitle(
QgsRenderContext *rendercontext, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
723 return drawTitleInternal( rendercontext,
nullptr, point, halignment, legendWidth );
726 QSizeF QgsLegendRenderer::drawAtom(
const Atom &atom,
QgsRenderContext *rendercontext, QPointF point )
728 return drawAtomInternal( atom, rendercontext,
nullptr, point );
733 return drawSymbolItemInternal( symbolItem, rendercontext,
nullptr, point, labelXOffset );
738 return drawLayerTitleInternal( nodeLayer, rendercontext,
nullptr, point );
743 return drawGroupTitleInternal( nodeGroup, rendercontext,
nullptr, point );
748 paintAndDetermineSize( &context );
751 QSizeF QgsLegendRenderer::paintAndDetermineSize(
QgsRenderContext *context )
753 return paintAndDetermineSizeInternal( context,
nullptr );
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
Layer tree group node serves as a container for layers and further groups.
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
QgsRenderContext * context
Render context, if available.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
QStringList evaluateItemText(const QString &text, const QgsExpressionContext &context) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
void drawLegend(QPainter *painter)
Draws the legend with given painter.
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QFont font() const
The font for this style.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Should not happen, only if corrupted project file.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
QPainter * painter
Painter.
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Constructor for QgsLegendRenderer.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file...
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
This class is a base class for nodes in a layer tree.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or null if none is enabled) ...
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Special style, item is hidden including margins around.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non-null pointer.
QPointF point
Top-left corner of the legend item.
bool equalColumnWidth() const
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
double columnSpace() const
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
double lineSpacing() const
double labelXOffset
offset from the left side where label should start
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.