31 : mLegendModel( legendModel )
32 , mSettings( settings )
38 return paintAndDetermineSize(
nullptr );
43 paintAndDetermineSize( painter );
47 QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter )
51 if ( !rootGroup )
return size;
53 QList<Atom> atomList = createAtomList( rootGroup, mSettings.
splitLayer() );
55 setColumns( atomList );
57 qreal maxColumnWidth = 0;
60 Q_FOREACH (
const Atom &atom, atomList )
62 maxColumnWidth = std::max( atom.size.width(), maxColumnWidth );
67 QSizeF titleSize = drawTitle();
69 titleSize.rwidth() += mSettings.
boxSpace() * 2.0;
72 QPointF point( mSettings.
boxSpace(), columnTop );
73 bool firstInColumn =
true;
74 double columnMaxHeight = 0;
75 qreal columnWidth = 0;
77 Q_FOREACH (
const Atom &atom, atomList )
79 if ( atom.column > column )
84 point.rx() += mSettings.
columnSpace() + maxColumnWidth;
88 point.rx() += mSettings.
columnSpace() + columnWidth;
90 point.ry() = columnTop;
97 point.ry() += spaceAboveAtom( atom );
100 QSizeF atomSize = drawAtom( atom, painter, point );
101 columnWidth = std::max( atomSize.width(), columnWidth );
103 point.ry() += atom.size.height();
104 columnMaxHeight = std::max( point.y() - columnTop, columnMaxHeight );
106 firstInColumn =
false;
108 point.rx() += columnWidth + mSettings.
boxSpace();
110 size.rheight() = columnTop + columnMaxHeight + mSettings.
boxSpace();
111 size.rwidth() = point.x();
112 if ( !mSettings.
title().isEmpty() )
114 size.rwidth() = std::max( titleSize.width(), size.width() );
118 if ( mLegendSize.isValid() )
120 qreal w = std::max( size.width(), mLegendSize.width() );
121 qreal h = std::max( size.height(), mLegendSize.height() );
122 size = QSizeF( w, h );
126 if ( !mSettings.
title().isEmpty() )
134 point.rx() = size.width() / 2;
138 point.rx() = size.width() - mSettings.
boxSpace();
141 drawTitle( painter, point, mSettings.
titleAlignment(), size.width() );
148 QList<QgsLegendRenderer::Atom> QgsLegendRenderer::createAtomList(
QgsLayerTreeGroup *parentGroup,
bool splitLayer )
152 if ( !parentGroup )
return atoms;
161 QList<Atom> groupAtoms = createAtomList( nodeGroup, splitLayer );
162 bool hasSubItems = !groupAtoms.empty();
168 nucleon.size = drawGroupTitle( nodeGroup );
170 if ( !groupAtoms.isEmpty() )
173 groupAtoms[0].size.rheight() += spaceAboveAtom( groupAtoms[0] );
175 groupAtoms[0].nucleons.prepend( nucleon );
176 groupAtoms[0].size.rheight() += nucleon.size.height();
177 groupAtoms[0].size.rwidth() = std::max( nucleon.size.width(), groupAtoms[0].size.width() );
183 atom.nucleons.append( nucleon );
184 atom.size.rwidth() += nucleon.size.width();
185 atom.size.rheight() += nucleon.size.height();
186 atom.size.rwidth() = std::max( nucleon.size.width(), atom.size.width() );
187 groupAtoms.append( atom );
193 atoms.append( groupAtoms );
207 nucleon.size = drawLayerTitle( nodeLayer );
208 atom.nucleons.append( nucleon );
209 atom.size.rwidth() = nucleon.size.width();
210 atom.size.rheight() = nucleon.size.height();
213 QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->
layerLegendNodes( nodeLayer );
221 QList<Atom> layerAtoms;
223 for (
int j = 0; j < legendNodes.count(); j++ )
227 Nucleon symbolNucleon = drawSymbolItem( legendNode );
233 atom.size.rwidth() = std::max( symbolNucleon.size.width(), atom.size.width() );
235 if ( !atom.nucleons.isEmpty() )
240 atom.size.rheight() += symbolNucleon.size.height();
241 atom.nucleons.append( symbolNucleon );
246 symbolAtom.nucleons.append( symbolNucleon );
247 symbolAtom.size.rwidth() = symbolNucleon.size.width();
248 symbolAtom.size.rheight() = symbolNucleon.size.height();
249 layerAtoms.append( symbolAtom );
252 layerAtoms.prepend( atom );
253 atoms.append( layerAtoms );
261 void QgsLegendRenderer::setColumns( QList<Atom> &atomList )
266 double totalHeight = 0;
267 qreal maxAtomHeight = 0;
268 Q_FOREACH (
const Atom &atom, atomList )
270 totalHeight += spaceAboveAtom( atom );
271 totalHeight += atom.size.height();
272 maxAtomHeight = std::max( atom.size.height(), maxAtomHeight );
280 double maxColumnHeight = 0;
281 int currentColumn = 0;
282 int currentColumnAtomCount = 0;
283 double currentColumnHeight = 0;
284 double closedColumnsHeight = 0;
286 for (
int i = 0; i < atomList.size(); i++ )
289 double avgColumnHeight = ( totalHeight - closedColumnsHeight ) / ( mSettings.
columnCount() - currentColumn );
291 Atom atom = atomList.at( i );
292 double currentHeight = currentColumnHeight;
293 if ( currentColumnAtomCount > 0 )
294 currentHeight += spaceAboveAtom( atom );
295 currentHeight += atom.size.height();
297 bool canCreateNewColumn = ( currentColumnAtomCount > 0 )
298 && ( currentColumn < mSettings.
columnCount() - 1 );
300 bool shouldCreateNewColumn = ( currentHeight - avgColumnHeight ) > atom.size.height() / 2
301 && currentColumnAtomCount > 0
302 && currentHeight > maxAtomHeight
303 && currentHeight > maxColumnHeight;
307 shouldCreateNewColumn |= ( atomList.size() - i < mSettings.
columnCount() - currentColumn );
309 if ( canCreateNewColumn && shouldCreateNewColumn )
313 currentColumnAtomCount = 0;
314 closedColumnsHeight += currentColumnHeight;
315 currentColumnHeight = atom.size.height();
319 currentColumnHeight = currentHeight;
321 atomList[i].column = currentColumn;
322 currentColumnAtomCount++;
323 maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
327 QMap<QString, qreal> maxSymbolWidth;
328 for (
int i = 0; i < atomList.size(); i++ )
330 Atom &atom = atomList[i];
331 for (
int j = 0; j < atom.nucleons.size(); j++ )
335 QString key = QStringLiteral(
"%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column );
336 maxSymbolWidth[key] = std::max( atom.nucleons.at( j ).symbolSize.width(), maxSymbolWidth[key] );
340 for (
int i = 0; i < atomList.size(); i++ )
342 Atom &atom = atomList[i];
343 for (
int j = 0; j < atom.nucleons.size(); j++ )
347 QString key = QStringLiteral(
"%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column );
350 atom.nucleons[j].labelXOffset = maxSymbolWidth[key] + space;
351 atom.nucleons[j].size.rwidth() = maxSymbolWidth[key] + space + atom.nucleons.at( j ).labelSize.width();
358 QSizeF QgsLegendRenderer::drawTitle( QPainter *painter, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
361 if ( mSettings.
title().isEmpty() )
367 double y = point.y();
371 painter->setPen( mSettings.
fontColor() );
377 switch ( halignment )
379 case Qt::AlignHCenter:
380 textBoxWidth = ( std::min( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.
boxSpace() ) * 2.0;
381 textBoxLeft = point.x() - textBoxWidth / 2.;
385 textBoxWidth = point.x() - mSettings.
boxSpace();
389 textBoxLeft = point.x();
390 textBoxWidth = legendWidth - point.x() - mSettings.
boxSpace();
396 for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
403 QRectF r( textBoxLeft, y, textBoxWidth, height );
407 mSettings.
drawText( painter, r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
411 size.rwidth() = std::max( width, size.rwidth() );
414 if ( titlePart != ( lines.end() - 1 ) )
419 size.rheight() = y - point.y();
425 double QgsLegendRenderer::spaceAboveAtom(
const Atom &atom )
427 if ( atom.nucleons.isEmpty() )
return 0;
429 Nucleon nucleon = atom.nucleons.first();
431 if (
QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
435 else if (
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
439 else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( nucleon.item ) )
450 QSizeF QgsLegendRenderer::drawAtom(
const Atom &atom, QPainter *painter, QPointF point )
453 QSizeF size = QSizeF( atom.size );
454 Q_FOREACH (
const Nucleon &nucleon, atom.nucleons )
456 if (
QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
465 drawGroupTitle( groupItem, painter, point );
468 else if (
QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
477 drawLayerTitle( layerItem, painter, point );
487 Nucleon symbolNucleon = drawSymbolItem( legendNode, painter, point, nucleon.labelXOffset );
489 size.rwidth() = std::max( symbolNucleon.size.width(), size.width() );
491 point.ry() += nucleon.size.height();
498 QgsLegendRenderer::Nucleon QgsLegendRenderer::drawSymbolItem(
QgsLayerTreeModelLegendNode *symbolItem, QPainter *painter, QPointF point,
double labelXOffset )
508 nucleon.item = symbolItem;
512 double width = std::max( static_cast< double >( im.
symbolSize.width() ), labelXOffset ) + im.
labelSize.width();
514 nucleon.size = QSizeF( width, height );
519 QSizeF QgsLegendRenderer::drawLayerTitle(
QgsLayerTreeLayer *nodeLayer, QPainter *painter, QPointF point )
522 QModelIndex idx = mLegendModel->
node2index( nodeLayer );
525 if ( mLegendModel->
data( idx, Qt::DisplayRole ).toString().isEmpty() )
return size;
527 double y = point.y();
534 for ( QStringList::Iterator layerItemPart = lines.begin(); layerItemPart != lines.end(); ++layerItemPart )
537 if ( painter ) mSettings.
drawText( painter, point.x(), y, *layerItemPart, layerFont );
539 size.rwidth() = std::max( width, size.width() );
540 if ( layerItemPart != ( lines.end() - 1 ) )
545 size.rheight() = y - point.y();
551 QSizeF QgsLegendRenderer::drawGroupTitle(
QgsLayerTreeGroup *nodeGroup, QPainter *painter, QPointF point )
554 QModelIndex idx = mLegendModel->
node2index( nodeGroup );
556 double y = point.y();
558 if ( painter ) painter->setPen( mSettings.
fontColor() );
563 for ( QStringList::Iterator groupPart = lines.begin(); groupPart != lines.end(); ++groupPart )
566 if ( painter ) mSettings.
drawText( painter, point.x(), y, *groupPart, groupFont );
568 size.rwidth() = std::max( width, size.width() );
569 if ( groupPart != ( lines.end() - 1 ) )
574 size.rheight() = y - point.y();
582 QString style = node->
customProperty( QStringLiteral(
"legend/title-style" ) ).toString();
583 if ( style == QLatin1String(
"hidden" ) )
585 else if ( style == QLatin1String(
"group" ) )
587 else if ( style == QLatin1String(
"subgroup" ) )
614 str = QStringLiteral(
"hidden" );
617 str = QStringLiteral(
"group" );
620 str = QStringLiteral(
"subgroup" );
626 if ( !str.isEmpty() )
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Layer tree group node serves as a container for layers and further groups.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
double lineSpacing() const
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non-null pointer.
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
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 QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
void drawLegend(QPainter *painter)
Draw the legend with given painter.
double columnSpace() const
Should not happen, only if corrupted project file.
bool equalColumnWidth() const
QPainter * painter
Painter.
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Construct legend renderer. The ownership of legend model does not change.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
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.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
QSizeF minimumSize()
Run the layout algorithm and determine the size required for legend.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
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...
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or null if none is enabled) ...
QColor layerFontColor() const
Returns layer font color, defaults to fontColor()
Special style, item is hidden including margins around.
QPointF point
Top-left corner of the legend item.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
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...
QFont font() const
The font for this style.
double labelXOffset
offset from the left side where label should start
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.
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...