QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgslegendrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslegendrenderer.cpp
3  --------------------------------------
4  Date : July 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgslegendrenderer.h"
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreemodel.h"
21 #include "qgslegendstyle.h"
22 #include "qgsmaplayerlegend.h"
23 #include "qgssymbol.h"
24 #include "qgsrendercontext.h"
25 #include "qgsvectorlayer.h"
27 
28 #include <QJsonObject>
29 #include <QPainter>
30 
31 
32 
34  : mLegendModel( legendModel )
35  , mSettings( settings )
36 {
37 }
38 
40 {
41  std::unique_ptr< QgsRenderContext > tmpContext;
42 
43  if ( !renderContext )
44  {
45  // QGIS 4.0 - make render context mandatory
47  tmpContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( nullptr ) ) );
48  tmpContext->setRendererScale( mSettings.mapScale() );
49  tmpContext->setMapToPixel( QgsMapToPixel( 1 / ( mSettings.mmPerMapUnit() * tmpContext->scaleFactor() ) ) );
50  renderContext = tmpContext.get();
52  }
53 
54  QgsScopedRenderContextPainterSwap nullPainterSwap( *renderContext, nullptr );
55  return paintAndDetermineSize( *renderContext );
56 }
57 
58 void QgsLegendRenderer::drawLegend( QPainter *painter )
59 {
62  QgsScopedRenderContextScaleToMm scaleToMm( context );
63 
64  context.setRendererScale( mSettings.mapScale() );
65  context.setMapToPixel( QgsMapToPixel( 1 / ( mSettings.mmPerMapUnit() * context.scaleFactor() ) ) );
67 
68  paintAndDetermineSize( context );
69 }
70 
72 {
73  QJsonObject json;
74 
75  QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
76  if ( !rootGroup )
77  return json;
78 
79  json = exportLegendToJson( context, rootGroup );
80  json[QStringLiteral( "title" )] = mSettings.title();
81  return json;
82 }
83 
84 QJsonObject QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup )
85 {
86  QJsonObject json;
87  QJsonArray nodes;
88  const QList<QgsLayerTreeNode *> childNodes = nodeGroup->children();
89  for ( QgsLayerTreeNode *node : childNodes )
90  {
91  if ( QgsLayerTree::isGroup( node ) )
92  {
93  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
94  const QModelIndex idx = mLegendModel->node2index( nodeGroup );
95  const QString text = mLegendModel->data( idx, Qt::DisplayRole ).toString();
96 
97  QJsonObject group = exportLegendToJson( context, nodeGroup );
98  group[ QStringLiteral( "type" ) ] = QStringLiteral( "group" );
99  group[ QStringLiteral( "title" ) ] = text;
100  nodes.append( group );
101  }
102  else if ( QgsLayerTree::isLayer( node ) )
103  {
104  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
105 
106  QString text;
107  if ( nodeLegendStyle( nodeLayer ) != QgsLegendStyle::Hidden )
108  {
109  const QModelIndex idx = mLegendModel->node2index( nodeLayer );
110  text = mLegendModel->data( idx, Qt::DisplayRole ).toString();
111  }
112 
113  QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
114 
115  if ( legendNodes.isEmpty() && mLegendModel->legendFilterMapSettings() )
116  continue;
117 
118  if ( legendNodes.count() == 1 )
119  {
120  QJsonObject group = legendNodes.at( 0 )->exportToJson( mSettings, context );
121  group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
122  nodes.append( group );
123  }
124  else if ( legendNodes.count() > 1 )
125  {
126  QJsonObject group;
127  group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
128  group[ QStringLiteral( "title" ) ] = text;
129 
130  QJsonArray symbols;
131  for ( int j = 0; j < legendNodes.count(); j++ )
132  {
133  QgsLayerTreeModelLegendNode *legendNode = legendNodes.at( j );
134  QJsonObject symbol = legendNode->exportToJson( mSettings, context );
135  symbols.append( symbol );
136  }
137  group[ QStringLiteral( "symbols" ) ] = symbols;
138 
139  nodes.append( group );
140  }
141  }
142  }
143 
144  json[QStringLiteral( "nodes" )] = nodes;
145  return json;
146 }
147 
148 QSizeF QgsLegendRenderer::paintAndDetermineSize( QgsRenderContext &context )
149 {
150  QSizeF size( 0, 0 );
151  QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
152  if ( !rootGroup )
153  return size;
154 
155  // temporarily remove painter from context -- we don't need to actually draw anything yet. But we DO need
156  // to send the full render context so that an expression context is available during the size calculation
157  QgsScopedRenderContextPainterSwap noPainter( context, nullptr );
158 
159  QList<LegendComponentGroup> componentGroups = createComponentGroupList( rootGroup, context );
160 
161  const int columnCount = setColumns( componentGroups );
162 
163  QMap< int, double > maxColumnWidths;
164  qreal maxEqualColumnWidth = 0;
165  // another iteration -- this one is required to calculate the maximum item width for each
166  // column. Unfortunately, we can't trust the component group widths at this stage, as they are minimal widths
167  // only. When actually rendering a symbol node, the text is aligned according to the WIDEST
168  // symbol in a column. So that means we can't possibly determine the exact size of legend components
169  // until now. BUUUUUUUUUUUUT. Because everything sucks, we can't even start the actual render of items
170  // at the same time we calculate this -- legend items REQUIRE the REAL width of the columns in order to
171  // correctly align right or center-aligned symbols/text. Bah -- A triple iteration it is!
172  for ( const LegendComponentGroup &group : qgis::as_const( componentGroups ) )
173  {
174  const QSizeF actualSize = drawGroup( group, context, ColumnContext() );
175  maxEqualColumnWidth = std::max( actualSize.width(), maxEqualColumnWidth );
176  maxColumnWidths[ group.column ] = std::max( actualSize.width(), maxColumnWidths.value( group.column, 0 ) );
177  }
178 
179  if ( columnCount == 1 )
180  {
181  // single column - use the full available width
182  maxEqualColumnWidth = std::max( maxEqualColumnWidth, mLegendSize.width() - 2 * mSettings.boxSpace() );
183  maxColumnWidths[ 0 ] = maxEqualColumnWidth;
184  }
185 
186  //calculate size of title
187  QSizeF titleSize = drawTitle( context, 0 );
188  //add title margin to size of title text
189  titleSize.rwidth() += mSettings.boxSpace() * 2.0;
190  double columnTop = mSettings.boxSpace() + titleSize.height() + mSettings.style( QgsLegendStyle::Title ).margin( QgsLegendStyle::Bottom );
191 
192  noPainter.reset();
193 
194  bool firstInColumn = true;
195  double columnMaxHeight = 0;
196  qreal columnWidth = 0;
197  int column = -1;
198  ColumnContext columnContext;
199  columnContext.left = mSettings.boxSpace();
200  columnContext.right = std::max( mLegendSize.width() - mSettings.boxSpace(), mSettings.boxSpace() );
201  double currentY = columnTop;
202 
203  for ( const LegendComponentGroup &group : qgis::as_const( componentGroups ) )
204  {
205  if ( group.column > column )
206  {
207  // Switch to next column
208  columnContext.left = group.column > 0 ? columnContext.right + mSettings.columnSpace() : mSettings.boxSpace();
209  columnWidth = mSettings.equalColumnWidth() ? maxEqualColumnWidth : maxColumnWidths.value( group.column );
210  columnContext.right = columnContext.left + columnWidth;
211  currentY = columnTop;
212  column++;
213  firstInColumn = true;
214  }
215  if ( !firstInColumn )
216  {
217  currentY += spaceAboveGroup( group );
218  }
219 
220  drawGroup( group, context, columnContext, currentY );
221 
222  currentY += group.size.height();
223  columnMaxHeight = std::max( currentY - columnTop, columnMaxHeight );
224 
225  firstInColumn = false;
226  }
227  const double totalWidth = columnContext.right + mSettings.boxSpace();
228 
229  size.rheight() = columnTop + columnMaxHeight + mSettings.boxSpace();
230  size.rwidth() = totalWidth;
231  if ( !mSettings.title().isEmpty() )
232  {
233  size.rwidth() = std::max( titleSize.width(), size.width() );
234  }
235 
236  // override the size if it was set by the user
237  if ( mLegendSize.isValid() )
238  {
239  qreal w = std::max( size.width(), mLegendSize.width() );
240  qreal h = std::max( size.height(), mLegendSize.height() );
241  size = QSizeF( w, h );
242  }
243 
244  // Now we have set the correct total item width and can draw the title centered
245  if ( !mSettings.title().isEmpty() )
246  {
247  drawTitle( context, mSettings.boxSpace(), mSettings.titleAlignment(), size.width() );
248  }
249 
250  return size;
251 }
252 
253 void QgsLegendRenderer::widthAndOffsetForTitleText( const Qt::AlignmentFlag halignment, const double legendWidth, double &textBoxWidth, double &textBoxLeft )
254 {
255  switch ( halignment )
256  {
257  default:
258  textBoxLeft = mSettings.boxSpace();
259  textBoxWidth = legendWidth - 2 * mSettings.boxSpace();
260  break;
261 
262  case Qt::AlignHCenter:
263  {
264  // not sure on this logic, I just moved it -- don't blame me for it being totally obscure!
265  const double centerX = legendWidth / 2;
266  textBoxWidth = ( std::min( static_cast< double >( centerX ), legendWidth - centerX ) - mSettings.boxSpace() ) * 2.0;
267  textBoxLeft = centerX - textBoxWidth / 2.;
268  break;
269  }
270  }
271 }
272 
273 QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponentGroupList( QgsLayerTreeGroup *parentGroup, QgsRenderContext &context )
274 {
275  QList<LegendComponentGroup> componentGroups;
276 
277  if ( !parentGroup )
278  return componentGroups;
279 
280  const QList<QgsLayerTreeNode *> childNodes = parentGroup->children();
281  for ( QgsLayerTreeNode *node : childNodes )
282  {
283  if ( QgsLayerTree::isGroup( node ) )
284  {
285  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
286 
287  // Group subitems
288  QList<LegendComponentGroup> subgroups = createComponentGroupList( nodeGroup, context );
289  bool hasSubItems = !subgroups.empty();
290 
291  if ( nodeLegendStyle( nodeGroup ) != QgsLegendStyle::Hidden )
292  {
293  LegendComponent component;
294  component.item = node;
295  component.size = drawGroupTitle( nodeGroup, context );
296 
297  if ( !subgroups.isEmpty() )
298  {
299  // Add internal space between this group title and the next component
300  subgroups[0].size.rheight() += spaceAboveGroup( subgroups[0] );
301  // Prepend this group title to the first group
302  subgroups[0].components.prepend( component );
303  subgroups[0].size.rheight() += component.size.height();
304  subgroups[0].size.rwidth() = std::max( component.size.width(), subgroups[0].size.width() );
305  if ( nodeGroup->customProperty( QStringLiteral( "legend/column-break" ) ).toInt() )
306  subgroups[0].placeColumnBreakBeforeGroup = true;
307  }
308  else
309  {
310  // no subitems, create new group
311  LegendComponentGroup group;
312  group.placeColumnBreakBeforeGroup = nodeGroup->customProperty( QStringLiteral( "legend/column-break" ) ).toInt();
313  group.components.append( component );
314  group.size.rwidth() += component.size.width();
315  group.size.rheight() += component.size.height();
316  group.size.rwidth() = std::max( component.size.width(), group.size.width() );
317  subgroups.append( group );
318  }
319  }
320 
321  if ( hasSubItems ) //leave away groups without content
322  {
323  componentGroups.append( subgroups );
324  }
325 
326  }
327  else if ( QgsLayerTree::isLayer( node ) )
328  {
329  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
330 
331  bool allowColumnSplit = false;
332  switch ( nodeLayer->legendSplitBehavior() )
333  {
335  allowColumnSplit = mSettings.splitLayer();
336  break;
338  allowColumnSplit = true;
339  break;
341  allowColumnSplit = false;
342  break;
343  }
344 
345  LegendComponentGroup group;
346  group.placeColumnBreakBeforeGroup = nodeLayer->customProperty( QStringLiteral( "legend/column-break" ) ).toInt();
347 
348  if ( nodeLegendStyle( nodeLayer ) != QgsLegendStyle::Hidden )
349  {
350  LegendComponent component;
351  component.item = node;
352  component.size = drawLayerTitle( nodeLayer, context );
353  group.components.append( component );
354  group.size.rwidth() = component.size.width();
355  group.size.rheight() = component.size.height();
356  }
357 
358  QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
359 
360  // workaround for the issue that "filtering by map" does not remove layer nodes that have no symbols present
361  // on the map. We explicitly skip such layers here. In future ideally that should be handled directly
362  // in the layer tree model
363  if ( legendNodes.isEmpty() && mLegendModel->legendFilterMapSettings() )
364  continue;
365 
366  QList<LegendComponentGroup> layerGroups;
367  layerGroups.reserve( legendNodes.count() );
368 
369  bool groupIsLayerGroup = true;
370 
371  for ( int j = 0; j < legendNodes.count(); j++ )
372  {
373  QgsLayerTreeModelLegendNode *legendNode = legendNodes.at( j );
374 
375  LegendComponent symbolComponent = drawSymbolItem( legendNode, context, ColumnContext(), 0 );
376 
377  const bool forceBreak = legendNode->columnBreak();
378 
379  if ( !allowColumnSplit || j == 0 )
380  {
381  if ( forceBreak )
382  {
383  if ( groupIsLayerGroup )
384  layerGroups.prepend( group );
385  else
386  layerGroups.append( group );
387 
388  group = LegendComponentGroup();
389  group.placeColumnBreakBeforeGroup = true;
390  groupIsLayerGroup = false;
391  }
392 
393  // append to layer group
394  // the width is not correct at this moment, we must align all symbol labels
395  group.size.rwidth() = std::max( symbolComponent.size.width(), group.size.width() );
396  // Add symbol space only if there is already title or another item above
397  if ( !group.components.isEmpty() )
398  {
399  // TODO: for now we keep Symbol and SymbolLabel Top margin in sync
400  group.size.rheight() += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
401  }
402  group.size.rheight() += symbolComponent.size.height();
403  group.components.append( symbolComponent );
404  }
405  else
406  {
407  if ( group.size.height() > 0 )
408  {
409  if ( groupIsLayerGroup )
410  layerGroups.prepend( group );
411  else
412  layerGroups.append( group );
413  group = LegendComponentGroup();
414  groupIsLayerGroup = false;
415  }
416  LegendComponentGroup symbolGroup;
417  symbolGroup.placeColumnBreakBeforeGroup = forceBreak;
418  symbolGroup.components.append( symbolComponent );
419  symbolGroup.size.rwidth() = symbolComponent.size.width();
420  symbolGroup.size.rheight() = symbolComponent.size.height();
421  layerGroups.append( symbolGroup );
422  }
423  }
424  if ( group.size.height() > 0 )
425  {
426  if ( groupIsLayerGroup )
427  layerGroups.prepend( group );
428  else
429  layerGroups.append( group );
430  }
431  componentGroups.append( layerGroups );
432  }
433  }
434 
435  return componentGroups;
436 }
437 
438 
439 int QgsLegendRenderer::setColumns( QList<LegendComponentGroup> &componentGroups )
440 {
441  // Divide groups to columns
442  double totalHeight = 0;
443  qreal maxGroupHeight = 0;
444  int forcedColumnBreaks = 0;
445  for ( const LegendComponentGroup &group : qgis::as_const( componentGroups ) )
446  {
447  totalHeight += spaceAboveGroup( group );
448  totalHeight += group.size.height();
449  maxGroupHeight = std::max( group.size.height(), maxGroupHeight );
450 
451  if ( group.placeColumnBreakBeforeGroup )
452  forcedColumnBreaks++;
453  }
454 
455  if ( mSettings.columnCount() == 0 && forcedColumnBreaks == 0 )
456  return 0;
457 
458  // the target number of columns allowed is dictated by the number of forced column
459  // breaks OR the manually set column count (whichever is greater!)
460  const int targetNumberColumns = std::max( forcedColumnBreaks + 1, mSettings.columnCount() );
461  const int numberAutoPlacedBreaks = targetNumberColumns - forcedColumnBreaks - 1;
462 
463  // We know height of each group and we have to split them into columns
464  // minimizing max column height. It is sort of bin packing problem, NP-hard.
465  // We are using simple heuristic, brute fore appeared to be to slow,
466  // the number of combinations is N = n!/(k!*(n-k)!) where n = groupCount-1
467  // and k = columnsCount-1
468  double maxColumnHeight = 0;
469  int currentColumn = 0;
470  int currentColumnGroupCount = 0; // number of groups in current column
471  double currentColumnHeight = 0;
472  double closedColumnsHeight = 0;
473  int autoPlacedBreaks = 0;
474 
475  for ( int i = 0; i < componentGroups.size(); i++ )
476  {
477  // Recalc average height for remaining columns including current
478  double avgColumnHeight = ( totalHeight - closedColumnsHeight ) / ( numberAutoPlacedBreaks + 1 - autoPlacedBreaks );
479 
480  LegendComponentGroup group = componentGroups.at( i );
481  double currentHeight = currentColumnHeight;
482  if ( currentColumnGroupCount > 0 )
483  currentHeight += spaceAboveGroup( group );
484  currentHeight += group.size.height();
485 
486  bool canCreateNewColumn = ( currentColumnGroupCount > 0 ) // do not leave empty column
487  && ( currentColumn < targetNumberColumns - 1 ) // must not exceed max number of columns
488  && ( autoPlacedBreaks < numberAutoPlacedBreaks );
489 
490  bool shouldCreateNewColumn = ( currentHeight - avgColumnHeight ) > group.size.height() / 2 // center of current group is over average height
491  && currentColumnGroupCount > 0 // do not leave empty column
492  && currentHeight > maxGroupHeight // no sense to make smaller columns than max group height
493  && currentHeight > maxColumnHeight; // no sense to make smaller columns than max column already created
494 
495  shouldCreateNewColumn |= group.placeColumnBreakBeforeGroup;
496  canCreateNewColumn |= group.placeColumnBreakBeforeGroup;
497 
498  // also should create a new column if the number of items left < number of columns left
499  // in this case we should spread the remaining items out over the remaining columns
500  shouldCreateNewColumn |= ( componentGroups.size() - i < targetNumberColumns - currentColumn );
501 
502  if ( canCreateNewColumn && shouldCreateNewColumn )
503  {
504  // New column
505  currentColumn++;
506  if ( !group.placeColumnBreakBeforeGroup )
507  autoPlacedBreaks++;
508  currentColumnGroupCount = 0;
509  closedColumnsHeight += currentColumnHeight;
510  currentColumnHeight = group.size.height();
511  }
512  else
513  {
514  currentColumnHeight = currentHeight;
515  }
516  componentGroups[i].column = currentColumn;
517  currentColumnGroupCount++;
518  maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
519  }
520 
521  // Align labels of symbols for each layer/column to the same labelXOffset
522  QMap<QString, qreal> maxSymbolWidth;
523  for ( int i = 0; i < componentGroups.size(); i++ )
524  {
525  LegendComponentGroup &group = componentGroups[i];
526  for ( int j = 0; j < group.components.size(); j++ )
527  {
528  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( group.components.at( j ).item ) )
529  {
530  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( group.column );
531  maxSymbolWidth[key] = std::max( group.components.at( j ).symbolSize.width(), maxSymbolWidth[key] );
532  }
533  }
534  }
535  for ( int i = 0; i < componentGroups.size(); i++ )
536  {
537  LegendComponentGroup &group = componentGroups[i];
538  for ( int j = 0; j < group.components.size(); j++ )
539  {
540  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( group.components.at( j ).item ) )
541  {
542  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( group.column );
543  double space = mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Right ) +
545  group.components[j].labelXOffset = maxSymbolWidth[key] + space;
546  group.components[j].maxSiblingSymbolWidth = maxSymbolWidth[key];
547  group.components[j].size.rwidth() = maxSymbolWidth[key] + space + group.components.at( j ).labelSize.width();
548  }
549  }
550  }
551  return targetNumberColumns;
552 }
553 
554 QSizeF QgsLegendRenderer::drawTitle( QgsRenderContext &context, double top, Qt::AlignmentFlag halignment, double legendWidth )
555 {
556  QSizeF size( 0, 0 );
557  if ( mSettings.title().isEmpty() )
558  {
559  return size;
560  }
561 
562  QStringList lines = mSettings.splitStringForWrapping( mSettings.title() );
563  double y = top;
564 
565  if ( context.painter() )
566  {
567  context.painter()->setPen( mSettings.fontColor() );
568  }
569 
570  //calculate width and left pos of rectangle to draw text into
571  double textBoxWidth;
572  double textBoxLeft;
573  widthAndOffsetForTitleText( halignment, legendWidth, textBoxWidth, textBoxLeft );
574 
575  QFont titleFont = mSettings.style( QgsLegendStyle::Title ).font();
576 
577  for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
578  {
579  //last word is not drawn if rectangle width is exactly text width, so add 1
580  //TODO - correctly calculate size of italicized text, since QFontMetrics does not
581  qreal width = mSettings.textWidthMillimeters( titleFont, *titlePart ) + 1;
582  qreal height = mSettings.fontAscentMillimeters( titleFont ) + mSettings.fontDescentMillimeters( titleFont );
583 
584  QRectF r( textBoxLeft, y, textBoxWidth, height );
585 
586  if ( context.painter() )
587  {
588  mSettings.drawText( context.painter(), r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
589  }
590 
591  //update max width of title
592  size.rwidth() = std::max( width, size.rwidth() );
593 
594  y += height;
595  if ( titlePart != ( lines.end() - 1 ) )
596  {
597  y += mSettings.lineSpacing();
598  }
599  }
600  size.rheight() = y - top;
601 
602  return size;
603 }
604 
605 
606 double QgsLegendRenderer::spaceAboveGroup( const LegendComponentGroup &group )
607 {
608  if ( group.components.isEmpty() ) return 0;
609 
610  LegendComponent component = group.components.first();
611 
612  if ( QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
613  {
614  return mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Top );
615  }
616  else if ( QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
617  {
618  return mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Top );
619  }
620  else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
621  {
622  // TODO: use Symbol or SymbolLabel Top margin
624  }
625 
626  return 0;
627 }
628 
629 QSizeF QgsLegendRenderer::drawGroup( const LegendComponentGroup &group, QgsRenderContext &context, ColumnContext columnContext, double top )
630 {
631  bool first = true;
632  QSizeF size = QSizeF( group.size );
633  double currentY = top;
634  for ( const LegendComponent &component : qgis::as_const( group.components ) )
635  {
636  if ( QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
637  {
638  QgsLegendStyle::Style s = nodeLegendStyle( groupItem );
639  if ( s != QgsLegendStyle::Hidden )
640  {
641  if ( !first )
642  {
643  currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
644  }
645  QSizeF groupSize;
646  groupSize = drawGroupTitle( groupItem, context, columnContext, currentY );
647  size.rwidth() = std::max( groupSize.width(), size.width() );
648  }
649  }
650  else if ( QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
651  {
652  QgsLegendStyle::Style s = nodeLegendStyle( layerItem );
653  if ( s != QgsLegendStyle::Hidden )
654  {
655  if ( !first )
656  {
657  currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
658  }
659  QSizeF subGroupSize;
660  subGroupSize = drawLayerTitle( layerItem, context, columnContext, currentY );
661  size.rwidth() = std::max( subGroupSize.width(), size.width() );
662  }
663  }
664  else if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
665  {
666  if ( !first )
667  {
668  currentY += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
669  }
670 
671  LegendComponent symbolComponent = drawSymbolItem( legendNode, context, columnContext, currentY, component.maxSiblingSymbolWidth );
672  // expand width, it may be wider because of label offsets
673  size.rwidth() = std::max( symbolComponent.size.width(), size.width() );
674  }
675  currentY += component.size.height();
676  first = false;
677  }
678  return size;
679 }
680 
681 QgsLegendRenderer::LegendComponent QgsLegendRenderer::drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, QgsRenderContext &context, ColumnContext columnContext, double top, double maxSiblingSymbolWidth )
682 {
684  ctx.context = &context;
685 
686  // add a layer expression context scope
687  QgsExpressionContextScope *layerScope = nullptr;
688  if ( symbolItem->layerNode()->layer() )
689  {
690  layerScope = QgsExpressionContextUtils::layerScope( symbolItem->layerNode()->layer() );
691  context.expressionContext().appendScope( layerScope );
692  }
693 
694  ctx.painter = context.painter();
696  ctx.point = QPointF( columnContext.left, top );
697  ctx.labelXOffset = maxSiblingSymbolWidth;
699 
700  ctx.top = top;
701 
702  ctx.columnLeft = columnContext.left;
703  ctx.columnRight = columnContext.right;
704 
705  switch ( mSettings.symbolAlignment() )
706  {
707  case Qt::AlignLeft:
708  default:
710  break;
711 
712  case Qt::AlignRight:
714  break;
715  }
716 
717  ctx.maxSiblingSymbolWidth = maxSiblingSymbolWidth;
718 
719  if ( const QgsSymbolLegendNode *symbolNode = dynamic_cast< const QgsSymbolLegendNode * >( symbolItem ) )
720  ctx.patchShape = symbolNode->patchShape();
721 
722  ctx.patchSize = symbolItem->userPatchSize();
723 
724  QgsLayerTreeModelLegendNode::ItemMetrics im = symbolItem->draw( mSettings, &ctx );
725 
726  if ( layerScope )
727  delete context.expressionContext().popScope();
728 
729  LegendComponent component;
730  component.item = symbolItem;
731  component.symbolSize = im.symbolSize;
732  component.labelSize = im.labelSize;
733  //QgsDebugMsg( QStringLiteral( "symbol height = %1 label height = %2").arg( symbolSize.height()).arg( labelSize.height() ));
734  // NOTE -- we hard code left/right margins below, because those are the only ones exposed for use currently.
735  // ideally we could (should?) expose all these margins as settings, and then adapt the below to respect the current symbol/text alignment
736  // and consider the correct margin sides...
737  double width = std::max( static_cast< double >( im.symbolSize.width() ), maxSiblingSymbolWidth )
741  + im.labelSize.width();
742 
743  double height = std::max( im.symbolSize.height(), im.labelSize.height() );
744  component.size = QSizeF( width, height );
745  return component;
746 }
747 
748 QSizeF QgsLegendRenderer::drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QgsRenderContext &context, ColumnContext columnContext, double top )
749 {
750  QSizeF size( 0, 0 );
751  QModelIndex idx = mLegendModel->node2index( nodeLayer );
752  QString titleString = mLegendModel->data( idx, Qt::DisplayRole ).toString();
753  //Let the user omit the layer title item by having an empty layer title string
754  if ( titleString.isEmpty() )
755  return size;
756 
757  double y = top;
758 
759  if ( context.painter() )
760  context.painter()->setPen( mSettings.layerFontColor() );
761 
762  QFont layerFont = mSettings.style( nodeLegendStyle( nodeLayer ) ).font();
763 
764  QgsExpressionContextScope *layerScope = nullptr;
765  if ( nodeLayer->layer() )
766  {
767  layerScope = QgsExpressionContextUtils::layerScope( nodeLayer->layer() );
768  context.expressionContext().appendScope( layerScope );
769  }
770 
771  const QStringList lines = mSettings.evaluateItemText( titleString, context.expressionContext() );
772  int i = 0;
773 
774  const double sideMargin = mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Left );
775  for ( QStringList::ConstIterator layerItemPart = lines.constBegin(); layerItemPart != lines.constEnd(); ++layerItemPart )
776  {
777  y += mSettings.fontAscentMillimeters( layerFont );
778  if ( QPainter *destPainter = context.painter() )
779  {
780  double x = columnContext.left + sideMargin;
781  if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() != Qt::AlignLeft )
782  {
783  const double labelWidth = mSettings.textWidthMillimeters( layerFont, *layerItemPart );
784  if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignRight )
785  x = columnContext.right - labelWidth - sideMargin;
786  else if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignHCenter )
787  x = columnContext.left + ( columnContext.right - columnContext.left - labelWidth ) / 2;
788  }
789  mSettings.drawText( destPainter, x, y, *layerItemPart, layerFont );
790  }
791  qreal width = mSettings.textWidthMillimeters( layerFont, *layerItemPart ) + sideMargin *
792  ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignHCenter ? 2 : 1 );
793  size.rwidth() = std::max( width, size.width() );
794  if ( layerItemPart != ( lines.end() - 1 ) )
795  {
796  y += mSettings.lineSpacing();
797  }
798  i++;
799  }
800  size.rheight() = y - top;
801  size.rheight() += mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Side::Bottom );
802 
803  if ( layerScope )
804  delete context.expressionContext().popScope();
805 
806  return size;
807 }
808 
809 QSizeF QgsLegendRenderer::drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QgsRenderContext &context, ColumnContext columnContext, double top )
810 {
811  QSizeF size( 0, 0 );
812  QModelIndex idx = mLegendModel->node2index( nodeGroup );
813 
814  double y = top;
815 
816  if ( context.painter() )
817  context.painter()->setPen( mSettings.fontColor() );
818 
819  QFont groupFont = mSettings.style( nodeLegendStyle( nodeGroup ) ).font();
820 
821  const double sideMargin = mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Left );
822 
823  const QStringList lines = mSettings.evaluateItemText( mLegendModel->data( idx, Qt::DisplayRole ).toString(), context.expressionContext() );
824  for ( QStringList::ConstIterator groupPart = lines.constBegin(); groupPart != lines.constEnd(); ++groupPart )
825  {
826  y += mSettings.fontAscentMillimeters( groupFont );
827 
828  if ( QPainter *destPainter = context.painter() )
829  {
830  double x = columnContext.left + sideMargin;
831  if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() != Qt::AlignLeft )
832  {
833  const double labelWidth = mSettings.textWidthMillimeters( groupFont, *groupPart );
834  if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignRight )
835  x = columnContext.right - labelWidth - sideMargin;
836  else if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignHCenter )
837  x = columnContext.left + ( columnContext.right - columnContext.left - labelWidth ) / 2;
838  }
839  mSettings.drawText( destPainter, x, y, *groupPart, groupFont );
840  }
841  qreal width = mSettings.textWidthMillimeters( groupFont, *groupPart ) + sideMargin * ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignHCenter ? 2 : 1 );
842  size.rwidth() = std::max( width, size.width() );
843  if ( groupPart != ( lines.end() - 1 ) )
844  {
845  y += mSettings.lineSpacing();
846  }
847  }
848  size.rheight() = y - top + mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Bottom );
849  return size;
850 }
851 
853 {
854  QString style = node->customProperty( QStringLiteral( "legend/title-style" ) ).toString();
855  if ( style == QLatin1String( "hidden" ) )
856  return QgsLegendStyle::Hidden;
857  else if ( style == QLatin1String( "group" ) )
858  return QgsLegendStyle::Group;
859  else if ( style == QLatin1String( "subgroup" ) )
861 
862  // use a default otherwise
863  if ( QgsLayerTree::isGroup( node ) )
864  return QgsLegendStyle::Group;
865  else if ( QgsLayerTree::isLayer( node ) )
866  {
867  if ( model->legendNodeEmbeddedInParent( QgsLayerTree::toLayer( node ) ) )
868  return QgsLegendStyle::Hidden;
870  }
871 
872  return QgsLegendStyle::Undefined; // should not happen, only if corrupted project file
873 }
874 
876 {
877  return nodeLegendStyle( node, mLegendModel );
878 }
879 
881 {
882  QString str;
883  switch ( style )
884  {
886  str = QStringLiteral( "hidden" );
887  break;
889  str = QStringLiteral( "group" );
890  break;
892  str = QStringLiteral( "subgroup" );
893  break;
894  default:
895  break; // nothing
896  }
897 
898  if ( !str.isEmpty() )
899  node->setCustomProperty( QStringLiteral( "legend/title-style" ), str );
900  else
901  node->removeCustomProperty( QStringLiteral( "legend/title-style" ) );
902 }
903 
905 {
906  paintAndDetermineSize( context );
907 }
908 
QgsLegendSettings::splitStringForWrapping
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
Definition: qgslegendsettings.cpp:96
qgsexpressioncontextutils.h
QgsLegendStyle::Symbol
@ Symbol
Symbol icon (excluding label)
Definition: qgslegendstyle.h:73
QgsLegendSettings::symbolAlignment
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
Definition: qgslegendsettings.h:255
QgsLegendSettings::title
QString title() const
Returns the title for the legend, which will be rendered above all legend items.
Definition: qgslegendsettings.h:55
QgsLegendStyle::Style
Style
Component of legends which can be styled.
Definition: qgslegendstyle.h:53
QgsLegendRenderer::setNodeLegendStyle
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
Definition: qgslegendrenderer.cpp:880
QgsLayerTreeNode
Definition: qgslayertreenode.h:74
QgsLegendSettings::columnCount
int columnCount() const
Returns the desired minimum number of columns to show in the legend.
Definition: qgslegendsettings.h:148
QgsLegendSettings::layerFontColor
QColor layerFontColor() const
Returns layer font color, defaults to fontColor()
Definition: qgslegendsettings.h:212
QgsLegendSettings::fontAscentMillimeters
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
Definition: qgslegendsettings.cpp:171
qgslayertreemodellegendnode.h
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:580
QgsLegendSettings::mmPerMapUnit
Q_DECL_DEPRECATED double mmPerMapUnit() const
Definition: qgslegendsettings.cpp:40
QgsLayerTreeModelLegendNode::ItemContext::columnRight
double columnRight
Right side of current legend column.
Definition: qgslayertreemodellegendnode.h:178
QgsExpressionContext::popScope
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
Definition: qgsexpressioncontext.cpp:500
QgsLegendSettings::textWidthMillimeters
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Definition: qgslegendsettings.cpp:157
QgsLegendRenderer::drawLegend
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
Definition: qgslegendrenderer.cpp:58
QgsLayerTreeModelLegendNode::ItemContext::patchSize
QSizeF patchSize
Symbol patch size to render for the node.
Definition: qgslayertreemodellegendnode.h:201
QgsLegendSettings::drawText
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
Definition: qgslegendsettings.cpp:115
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:264
QgsLayerTreeModelLegendNode::ItemContext::point
Q_DECL_DEPRECATED QPointF point
Top-left corner of the legend item.
Definition: qgslayertreemodellegendnode.h:150
QgsLegendSettings::mapScale
Q_DECL_DEPRECATED double mapScale() const
Returns the legend map scale.
Definition: qgslegendsettings.cpp:60
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsLegendSettings::style
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
Definition: qgslegendsettings.h:81
QgsLegendStyle::Bottom
@ Bottom
Bottom side.
Definition: qgslegendstyle.h:70
QgsLayerTreeModelLegendNode::exportToJson
QJsonObject exportToJson(const QgsLegendSettings &settings, const QgsRenderContext &context)
Entry point called from QgsLegendRenderer to do the rendering in a JSON object.
Definition: qgslayertreemodellegendnode.cpp:87
QgsLegendRenderer::nodeLegendStyle
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
Definition: qgslegendrenderer.cpp:852
QgsLayerTreeModelLegendNode::ItemContext::patchShape
QgsLegendPatchShape patchShape
The patch shape to render for the node.
Definition: qgslayertreemodellegendnode.h:192
QgsLayerTree::toLayer
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:88
QgsLayerTreeModel
Definition: qgslayertreemodel.h:53
QgsLayerTreeModel::legendNodeEmbeddedInParent
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
Definition: qgslayertreemodel.cpp:1518
QgsLegendStyle::SymbolLabel
@ SymbolLabel
Symbol label (excluding icon)
Definition: qgslegendstyle.h:74
QgsLayerTreeModelLegendNode::ItemContext::labelXOffset
Q_DECL_DEPRECATED double labelXOffset
Offset from the left side where label should start.
Definition: qgslayertreemodellegendnode.h:156
QgsLayerTree::toGroup
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:77
QgsLayerTreeModelLegendNode::draw
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
Definition: qgslayertreemodellegendnode.cpp:71
QgsLegendStyle::Title
@ Title
Legend title.
Definition: qgslegendstyle.h:70
QgsLegendRenderer::QgsLegendRenderer
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Constructor for QgsLegendRenderer.
Definition: qgslegendrenderer.cpp:33
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:752
QgsLayerTreeModelLegendNode::ItemMetrics::labelSize
QSizeF labelSize
Definition: qgslayertreemodellegendnode.h:207
QgsLayerTreeModel::node2index
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...
Definition: qgslayertreemodel.cpp:455
QgsLayerTreeLayer::UseDefaultLegendSetting
@ UseDefaultLegendSetting
Inherit default legend column splitting setting.
Definition: qgslayertreelayer.h:191
QgsLegendStyle::Undefined
@ Undefined
Should not happen, only if corrupted project file.
Definition: qgslegendstyle.h:68
QgsLayerTreeModelLegendNode::userPatchSize
virtual QSizeF userPatchSize() const
Returns the user (overridden) size for the legend node.
Definition: qgslayertreemodellegendnode.cpp:63
QgsLayerTreeLayer
Definition: qgslayertreelayer.h:43
QgsLegendSettings::boxSpace
double boxSpace() const
Returns the legend box space (in millimeters), which is the empty margin around the inside of the leg...
Definition: qgslegendsettings.h:96
QgsLayerTreeModel::legendFilterMapSettings
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
Definition: qgslayertreemodel.h:250
QgsLayerTreeGroup
Definition: qgslayertreegroup.h:34
qgsrendercontext.h
QgsLegendSettings
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
Definition: qgslegendsettings.h:38
QgsWms::legendModel
QgsLayerTreeModel * legendModel(const QgsWmsRenderContext &context, QgsLayerTree &tree)
Definition: qgswmsgetlegendgraphics.cpp:243
QgsLegendStyle::Right
@ Right
Right side.
Definition: qgslegendstyle.h:72
QgsLayerTreeLayer::AllowSplittingLegendNodesOverMultipleColumns
@ AllowSplittingLegendNodesOverMultipleColumns
Allow splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:192
QgsLayerTreeModelLegendNode::ItemContext
Definition: qgslayertreemodellegendnode.h:135
QgsLegendSettings::titleAlignment
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
Definition: qgslegendsettings.h:61
QgsLayerTreeLayer::layer
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Definition: qgslayertreelayer.h:74
qgslayertree.h
QgsLayerTreeModelLegendNode::ItemMetrics::symbolSize
QSizeF symbolSize
Definition: qgslayertreemodellegendnode.h:206
QgsScopedRenderContextScaleToMm
Definition: qgsrendercontext.h:956
QgsLayerTreeModelLegendNode::columnBreak
virtual bool columnBreak() const
Returns whether a forced column break should occur before the node.
Definition: qgslayertreemodellegendnode.h:126
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext....
Definition: qgsexpressioncontext.h:111
QgsLayerTreeModel::rootGroup
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
Definition: qgslayertreemodel.cpp:518
QgsLayerTreeLayer::PreventSplittingLegendNodesOverMultipleColumns
@ PreventSplittingLegendNodesOverMultipleColumns
Prevent splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:193
QgsWms::legendNode
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
Definition: qgswmsgetlegendgraphics.cpp:358
QgsLayerTreeModelLegendNode::ItemContext::maxSiblingSymbolWidth
double maxSiblingSymbolWidth
Largest symbol width, considering all other sibling legend components associated with the current com...
Definition: qgslayertreemodellegendnode.h:185
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:490
qgsvectorlayer.h
QgsScopedRenderContextPainterSwap
Definition: qgsrendercontext.h:900
QgsLegendStyle::Left
@ Left
Left side.
Definition: qgslegendstyle.h:71
QgsLayerTree::isLayer
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:66
QgsLayerTreeModelLegendNode::ItemMetrics
Definition: qgslayertreemodellegendnode.h:204
qgslegendstyle.h
QgsLayerTreeModelLegendNode::layerNode
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
Definition: qgslayertreemodellegendnode.h:70
QgsMapToPixel
Definition: qgsmaptopixel.h:37
QgsLayerTreeNode::setCustomProperty
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.
Definition: qgslayertreenode.cpp:180
QgsLegendStyle::font
QFont font() const
Returns the font used for rendering this legend component.
Definition: qgslegendstyle.h:81
QgsLayerTreeNode::customProperty
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.
Definition: qgslayertreenode.cpp:189
QgsRenderContext::fromQPainter
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
Definition: qgsrendercontext.cpp:111
QgsLayerTreeModelLegendNode::ItemContext::top
double top
Top y-position of legend item.
Definition: qgslayertreemodellegendnode.h:162
QgsLegendSettings::splitLayer
bool splitLayer() const
Returns true if layer components can be split over multiple columns.
Definition: qgslegendsettings.h:165
QgsLayerTreeNode::children
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Definition: qgslayertreenode.h:112
qgsmaplayerlegend.h
QgsLegendSettings::equalColumnWidth
bool equalColumnWidth() const
Returns true if all columns should have equal widths.
Definition: qgslegendsettings.h:181
QgsLayerTreeLayer::legendSplitBehavior
LegendNodesSplitBehavior legendSplitBehavior() const
Returns the column split behavior for the node.
Definition: qgslayertreelayer.h:204
qgslayertreemodel.h
QgsLayerTreeModelLegendNode::ItemContext::painter
QPainter * painter
Painter.
Definition: qgslayertreemodellegendnode.h:144
QgsLegendSettings::fontColor
QColor fontColor() const
Returns the font color used for legend items.
Definition: qgslegendsettings.h:197
QgsLegendSettings::lineSpacing
double lineSpacing() const
Returns the line spacing to use between lines of legend text.
Definition: qgslegendsettings.h:337
QgsLegendStyle::Hidden
@ Hidden
Special style, item is hidden including margins around.
Definition: qgslegendstyle.h:69
QgsLegendRenderer::minimumSize
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
Definition: qgslegendrenderer.cpp:39
QgsLegendSettings::fontDescentMillimeters
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
Definition: qgslegendsettings.cpp:178
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:174
QgsLayerTreeModel::layerLegendNodes
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...
Definition: qgslayertreemodel.cpp:1533
QgsLayerTreeModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: qgslayertreemodel.cpp:158
QgsSymbolLegendNode
Definition: qgslayertreemodellegendnode.h:283
QgsLayerTreeNode::removeCustomProperty
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
Definition: qgslayertreenode.cpp:194
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:751
QgsLegendStyle::Top
@ Top
Top side.
Definition: qgslegendstyle.h:69
QgsLayerTree::isGroup
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:56
qgslegendrenderer.h
QgsLegendSettings::columnSpace
double columnSpace() const
Returns the margin space between adjacent columns (in millimeters).
Definition: qgslegendsettings.h:131
QgsLegendStyle::Group
@ Group
Legend group title.
Definition: qgslegendstyle.h:71
QgsLegendStyle::Subgroup
@ Subgroup
Legend subgroup title.
Definition: qgslegendstyle.h:72
qgssymbol.h
QgsLayerTreeModelLegendNode::ItemContext::columnLeft
double columnLeft
Left side of current legend column.
Definition: qgslayertreemodellegendnode.h:170
QgsLegendRenderer::exportLegendToJson
QJsonObject exportLegendToJson(const QgsRenderContext &context)
Renders the legend in a json object.
Definition: qgslegendrenderer.cpp:71
QgsLayerTreeModelLegendNode::ItemContext::context
Q_NOWARN_DEPRECATED_POP QgsRenderContext * context
Render context, if available.
Definition: qgslayertreemodellegendnode.h:142
QgsLegendStyle::margin
double margin(Side side)
Returns the margin (in mm) for the specified side of the component.
Definition: qgslegendstyle.h:104
QgsLegendSettings::evaluateItemText
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...
Definition: qgslegendsettings.cpp:90
QgsLayerTreeModelLegendNode
Definition: qgslayertreemodellegendnode.h:50