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();
529 if ( painter ) painter->setPen( mSettings.
fontColor() );
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.
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.
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...
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
void drawLegend(QPainter *painter)
Draw 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.
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)
Construct legend renderer. The ownership of legend model does not change.
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)
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
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.
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.
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or null if none is enabled) ...
Special style, item is hidden including margins around.
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
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...
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.