QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  double totalSpaceAboveGroups = 0;
446  for ( const LegendComponentGroup &group : qgis::as_const( componentGroups ) )
447  {
448  totalHeight += spaceAboveGroup( group );
449  totalSpaceAboveGroups += spaceAboveGroup( group );
450  totalHeight += group.size.height();
451  maxGroupHeight = std::max( group.size.height(), maxGroupHeight );
452 
453  if ( group.placeColumnBreakBeforeGroup )
454  forcedColumnBreaks++;
455  }
456  double averageGroupHeight = ( totalHeight - totalSpaceAboveGroups ) / componentGroups.size();
457 
458  if ( mSettings.columnCount() == 0 && forcedColumnBreaks == 0 )
459  return 0;
460 
461  // the target number of columns allowed is dictated by the number of forced column
462  // breaks OR the manually set column count (whichever is greater!)
463  const int targetNumberColumns = std::max( forcedColumnBreaks + 1, mSettings.columnCount() );
464  const int numberAutoPlacedBreaks = targetNumberColumns - forcedColumnBreaks - 1;
465 
466  // We know height of each group and we have to split them into columns
467  // minimizing max column height. It is sort of bin packing problem, NP-hard.
468  // We are using simple heuristic, brute fore appeared to be to slow,
469  // the number of combinations is N = n!/(k!*(n-k)!) where n = groupCount-1
470  // and k = columnsCount-1
471  double maxColumnHeight = 0;
472  int currentColumn = 0;
473  int currentColumnGroupCount = 0; // number of groups in current column
474  double currentColumnHeight = 0;
475  double closedColumnsHeight = 0;
476  int autoPlacedBreaks = 0;
477 
478  // Calculate the expected average space between items
479  double averageSpaceAboveGroups = 0;
480  if ( componentGroups.size() > targetNumberColumns )
481  averageSpaceAboveGroups = totalSpaceAboveGroups / ( componentGroups.size() );
482  // Correct the totalHeight using the number of columns because the first item
483  // in each column does not get any space above it
484  totalHeight -= targetNumberColumns * averageSpaceAboveGroups;
485 
486  for ( int i = 0; i < componentGroups.size(); i++ )
487  {
488  LegendComponentGroup group = componentGroups.at( i );
489  double currentHeight = currentColumnHeight;
490  if ( currentColumnGroupCount > 0 )
491  currentHeight += spaceAboveGroup( group );
492  currentHeight += group.size.height();
493 
494  int numberRemainingGroups = componentGroups.size() - i;
495 
496  // Recalc average height for remaining columns including current
497  int numberRemainingColumns = numberAutoPlacedBreaks + 1 - autoPlacedBreaks;
498  double avgColumnHeight = ( currentHeight + numberRemainingGroups * averageGroupHeight + ( numberRemainingGroups - numberRemainingColumns - 1 ) * averageSpaceAboveGroups ) / numberRemainingColumns;
499  // Round up to the next full number of groups to put in one column
500  // This ensures that earlier columns contain more elements than later columns
501  int averageGroupsPerColumn = std::ceil( avgColumnHeight / ( averageGroupHeight + averageSpaceAboveGroups ) );
502  avgColumnHeight = averageGroupsPerColumn * ( averageGroupHeight + averageSpaceAboveGroups ) - averageSpaceAboveGroups;
503 
504  bool canCreateNewColumn = ( currentColumnGroupCount > 0 ) // do not leave empty column
505  && ( currentColumn < targetNumberColumns - 1 ) // must not exceed max number of columns
506  && ( autoPlacedBreaks < numberAutoPlacedBreaks );
507 
508  bool shouldCreateNewColumn = currentHeight > avgColumnHeight // current group height is greater than expected group height
509  && currentColumnGroupCount > 0 // do not leave empty column
510  && currentHeight > maxGroupHeight // no sense to make smaller columns than max group height
511  && currentHeight > maxColumnHeight; // no sense to make smaller columns than max column already created
512 
513  shouldCreateNewColumn |= group.placeColumnBreakBeforeGroup;
514  canCreateNewColumn |= group.placeColumnBreakBeforeGroup;
515 
516  // also should create a new column if the number of items left < number of columns left
517  // in this case we should spread the remaining items out over the remaining columns
518  shouldCreateNewColumn |= ( componentGroups.size() - i < targetNumberColumns - currentColumn );
519 
520  if ( canCreateNewColumn && shouldCreateNewColumn )
521  {
522  // New column
523  currentColumn++;
524  if ( !group.placeColumnBreakBeforeGroup )
525  autoPlacedBreaks++;
526  currentColumnGroupCount = 0;
527  closedColumnsHeight += currentColumnHeight;
528  currentColumnHeight = group.size.height();
529  }
530  else
531  {
532  currentColumnHeight = currentHeight;
533  }
534  componentGroups[i].column = currentColumn;
535  currentColumnGroupCount++;
536  maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
537  }
538 
539  // Align labels of symbols for each layer/column to the same labelXOffset
540  QMap<QString, qreal> maxSymbolWidth;
541  for ( int i = 0; i < componentGroups.size(); i++ )
542  {
543  LegendComponentGroup &group = componentGroups[i];
544  for ( int j = 0; j < group.components.size(); j++ )
545  {
546  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( group.components.at( j ).item ) )
547  {
548  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( group.column );
549  maxSymbolWidth[key] = std::max( group.components.at( j ).symbolSize.width(), maxSymbolWidth[key] );
550  }
551  }
552  }
553  for ( int i = 0; i < componentGroups.size(); i++ )
554  {
555  LegendComponentGroup &group = componentGroups[i];
556  for ( int j = 0; j < group.components.size(); j++ )
557  {
558  if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( group.components.at( j ).item ) )
559  {
560  QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( group.column );
561  double space = mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Right ) +
563  group.components[j].labelXOffset = maxSymbolWidth[key] + space;
564  group.components[j].maxSiblingSymbolWidth = maxSymbolWidth[key];
565  group.components[j].size.rwidth() = maxSymbolWidth[key] + space + group.components.at( j ).labelSize.width();
566  }
567  }
568  }
569  return targetNumberColumns;
570 }
571 
572 QSizeF QgsLegendRenderer::drawTitle( QgsRenderContext &context, double top, Qt::AlignmentFlag halignment, double legendWidth )
573 {
574  QSizeF size( 0, 0 );
575  if ( mSettings.title().isEmpty() )
576  {
577  return size;
578  }
579 
580  QStringList lines = mSettings.splitStringForWrapping( mSettings.title() );
581  double y = top;
582 
583  if ( auto *lPainter = context.painter() )
584  {
585  lPainter->setPen( mSettings.fontColor() );
586  }
587 
588  //calculate width and left pos of rectangle to draw text into
589  double textBoxWidth;
590  double textBoxLeft;
591  widthAndOffsetForTitleText( halignment, legendWidth, textBoxWidth, textBoxLeft );
592 
593  QFont titleFont = mSettings.style( QgsLegendStyle::Title ).font();
594 
595  for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
596  {
597  //last word is not drawn if rectangle width is exactly text width, so add 1
598  //TODO - correctly calculate size of italicized text, since QFontMetrics does not
599  qreal width = mSettings.textWidthMillimeters( titleFont, *titlePart ) + 1;
600  qreal height = mSettings.fontAscentMillimeters( titleFont ) + mSettings.fontDescentMillimeters( titleFont );
601 
602  QRectF r( textBoxLeft, y, textBoxWidth, height );
603 
604  if ( context.painter() )
605  {
606  mSettings.drawText( context.painter(), r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
607  }
608 
609  //update max width of title
610  size.rwidth() = std::max( width, size.rwidth() );
611 
612  y += height;
613  if ( titlePart != ( lines.end() - 1 ) )
614  {
615  y += mSettings.lineSpacing();
616  }
617  }
618  size.rheight() = y - top;
619 
620  return size;
621 }
622 
623 
624 double QgsLegendRenderer::spaceAboveGroup( const LegendComponentGroup &group )
625 {
626  if ( group.components.isEmpty() ) return 0;
627 
628  LegendComponent component = group.components.first();
629 
630  if ( QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
631  {
632  return mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Top );
633  }
634  else if ( QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
635  {
636  return mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Top );
637  }
638  else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
639  {
640  // TODO: use Symbol or SymbolLabel Top margin
642  }
643 
644  return 0;
645 }
646 
647 QSizeF QgsLegendRenderer::drawGroup( const LegendComponentGroup &group, QgsRenderContext &context, ColumnContext columnContext, double top )
648 {
649  bool first = true;
650  QSizeF size = QSizeF( group.size );
651  double currentY = top;
652  for ( const LegendComponent &component : qgis::as_const( group.components ) )
653  {
654  if ( QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
655  {
656  QgsLegendStyle::Style s = nodeLegendStyle( groupItem );
657  if ( s != QgsLegendStyle::Hidden )
658  {
659  if ( !first )
660  {
661  currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
662  }
663  QSizeF groupSize;
664  groupSize = drawGroupTitle( groupItem, context, columnContext, currentY );
665  size.rwidth() = std::max( groupSize.width(), size.width() );
666  }
667  }
668  else if ( QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
669  {
670  QgsLegendStyle::Style s = nodeLegendStyle( layerItem );
671  if ( s != QgsLegendStyle::Hidden )
672  {
673  if ( !first )
674  {
675  currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
676  }
677  QSizeF subGroupSize;
678  subGroupSize = drawLayerTitle( layerItem, context, columnContext, currentY );
679  size.rwidth() = std::max( subGroupSize.width(), size.width() );
680  }
681  }
682  else if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
683  {
684  if ( !first )
685  {
686  currentY += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
687  }
688 
689  LegendComponent symbolComponent = drawSymbolItem( legendNode, context, columnContext, currentY, component.maxSiblingSymbolWidth );
690  // expand width, it may be wider because of label offsets
691  size.rwidth() = std::max( symbolComponent.size.width(), size.width() );
692  }
693  currentY += component.size.height();
694  first = false;
695  }
696  return size;
697 }
698 
699 QgsLegendRenderer::LegendComponent QgsLegendRenderer::drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, QgsRenderContext &context, ColumnContext columnContext, double top, double maxSiblingSymbolWidth )
700 {
702  ctx.context = &context;
703 
704  // add a layer expression context scope
705  QgsExpressionContextScope *layerScope = nullptr;
706  if ( symbolItem->layerNode()->layer() )
707  {
708  layerScope = QgsExpressionContextUtils::layerScope( symbolItem->layerNode()->layer() );
709  context.expressionContext().appendScope( layerScope );
710  }
711 
712  ctx.painter = context.painter();
714  ctx.point = QPointF( columnContext.left, top );
715  ctx.labelXOffset = maxSiblingSymbolWidth;
717 
718  ctx.top = top;
719 
720  ctx.columnLeft = columnContext.left;
721  ctx.columnRight = columnContext.right;
722 
723  switch ( mSettings.symbolAlignment() )
724  {
725  case Qt::AlignLeft:
726  default:
728  break;
729 
730  case Qt::AlignRight:
732  break;
733  }
734 
735  ctx.maxSiblingSymbolWidth = maxSiblingSymbolWidth;
736 
737  if ( const QgsSymbolLegendNode *symbolNode = dynamic_cast< const QgsSymbolLegendNode * >( symbolItem ) )
738  ctx.patchShape = symbolNode->patchShape();
739 
740  ctx.patchSize = symbolItem->userPatchSize();
741 
742  QgsLayerTreeModelLegendNode::ItemMetrics im = symbolItem->draw( mSettings, &ctx );
743 
744  if ( layerScope )
745  delete context.expressionContext().popScope();
746 
747  LegendComponent component;
748  component.item = symbolItem;
749  component.symbolSize = im.symbolSize;
750  component.labelSize = im.labelSize;
751  //QgsDebugMsg( QStringLiteral( "symbol height = %1 label height = %2").arg( symbolSize.height()).arg( labelSize.height() ));
752  // NOTE -- we hard code left/right margins below, because those are the only ones exposed for use currently.
753  // ideally we could (should?) expose all these margins as settings, and then adapt the below to respect the current symbol/text alignment
754  // and consider the correct margin sides...
755  double width = std::max( static_cast< double >( im.symbolSize.width() ), maxSiblingSymbolWidth )
759  + im.labelSize.width();
760 
761  double height = std::max( im.symbolSize.height(), im.labelSize.height() );
762  component.size = QSizeF( width, height );
763  return component;
764 }
765 
766 QSizeF QgsLegendRenderer::drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QgsRenderContext &context, ColumnContext columnContext, double top )
767 {
768  QSizeF size( 0, 0 );
769  QModelIndex idx = mLegendModel->node2index( nodeLayer );
770  QString titleString = mLegendModel->data( idx, Qt::DisplayRole ).toString();
771  //Let the user omit the layer title item by having an empty layer title string
772  if ( titleString.isEmpty() )
773  return size;
774 
775  double y = top;
776 
777  if ( auto *lPainter = context.painter() )
778  lPainter->setPen( mSettings.layerFontColor() );
779 
780  QFont layerFont = mSettings.style( nodeLegendStyle( nodeLayer ) ).font();
781 
782  QgsExpressionContextScope *layerScope = nullptr;
783  if ( nodeLayer->layer() )
784  {
785  layerScope = QgsExpressionContextUtils::layerScope( nodeLayer->layer() );
786  context.expressionContext().appendScope( layerScope );
787  }
788 
789  const QStringList lines = mSettings.evaluateItemText( titleString, context.expressionContext() );
790  int i = 0;
791 
792  const double sideMargin = mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Left );
793  for ( QStringList::ConstIterator layerItemPart = lines.constBegin(); layerItemPart != lines.constEnd(); ++layerItemPart )
794  {
795  y += mSettings.fontAscentMillimeters( layerFont );
796  if ( QPainter *destPainter = context.painter() )
797  {
798  double x = columnContext.left + sideMargin;
799  if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() != Qt::AlignLeft )
800  {
801  const double labelWidth = mSettings.textWidthMillimeters( layerFont, *layerItemPart );
802  if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignRight )
803  x = columnContext.right - labelWidth - sideMargin;
804  else if ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignHCenter )
805  x = columnContext.left + ( columnContext.right - columnContext.left - labelWidth ) / 2;
806  }
807  mSettings.drawText( destPainter, x, y, *layerItemPart, layerFont );
808  }
809  qreal width = mSettings.textWidthMillimeters( layerFont, *layerItemPart ) + sideMargin *
810  ( mSettings.style( nodeLegendStyle( nodeLayer ) ).alignment() == Qt::AlignHCenter ? 2 : 1 );
811  size.rwidth() = std::max( width, size.width() );
812  if ( layerItemPart != ( lines.end() - 1 ) )
813  {
814  y += mSettings.lineSpacing();
815  }
816  i++;
817  }
818  size.rheight() = y - top;
819  size.rheight() += mSettings.style( nodeLegendStyle( nodeLayer ) ).margin( QgsLegendStyle::Side::Bottom );
820 
821  if ( layerScope )
822  delete context.expressionContext().popScope();
823 
824  return size;
825 }
826 
827 QSizeF QgsLegendRenderer::drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QgsRenderContext &context, ColumnContext columnContext, double top )
828 {
829  QSizeF size( 0, 0 );
830  QModelIndex idx = mLegendModel->node2index( nodeGroup );
831 
832  double y = top;
833 
834  if ( auto *lPainter = context.painter() )
835  lPainter->setPen( mSettings.fontColor() );
836 
837  QFont groupFont = mSettings.style( nodeLegendStyle( nodeGroup ) ).font();
838 
839  const double sideMargin = mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Left );
840 
841  const QStringList lines = mSettings.evaluateItemText( mLegendModel->data( idx, Qt::DisplayRole ).toString(), context.expressionContext() );
842  for ( QStringList::ConstIterator groupPart = lines.constBegin(); groupPart != lines.constEnd(); ++groupPart )
843  {
844  y += mSettings.fontAscentMillimeters( groupFont );
845 
846  if ( QPainter *destPainter = context.painter() )
847  {
848  double x = columnContext.left + sideMargin;
849  if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() != Qt::AlignLeft )
850  {
851  const double labelWidth = mSettings.textWidthMillimeters( groupFont, *groupPart );
852  if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignRight )
853  x = columnContext.right - labelWidth - sideMargin;
854  else if ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignHCenter )
855  x = columnContext.left + ( columnContext.right - columnContext.left - labelWidth ) / 2;
856  }
857  mSettings.drawText( destPainter, x, y, *groupPart, groupFont );
858  }
859  qreal width = mSettings.textWidthMillimeters( groupFont, *groupPart ) + sideMargin * ( mSettings.style( nodeLegendStyle( nodeGroup ) ).alignment() == Qt::AlignHCenter ? 2 : 1 );
860  size.rwidth() = std::max( width, size.width() );
861  if ( groupPart != ( lines.end() - 1 ) )
862  {
863  y += mSettings.lineSpacing();
864  }
865  }
866  size.rheight() = y - top + mSettings.style( nodeLegendStyle( nodeGroup ) ).margin( QgsLegendStyle::Bottom );
867  return size;
868 }
869 
871 {
872  QString style = node->customProperty( QStringLiteral( "legend/title-style" ) ).toString();
873  if ( style == QLatin1String( "hidden" ) )
874  return QgsLegendStyle::Hidden;
875  else if ( style == QLatin1String( "group" ) )
876  return QgsLegendStyle::Group;
877  else if ( style == QLatin1String( "subgroup" ) )
879 
880  // use a default otherwise
881  if ( QgsLayerTree::isGroup( node ) )
882  return QgsLegendStyle::Group;
883  else if ( QgsLayerTree::isLayer( node ) )
884  {
885  if ( model->legendNodeEmbeddedInParent( QgsLayerTree::toLayer( node ) ) )
886  return QgsLegendStyle::Hidden;
888  }
889 
890  return QgsLegendStyle::Undefined; // should not happen, only if corrupted project file
891 }
892 
894 {
895  return nodeLegendStyle( node, mLegendModel );
896 }
897 
899 {
900  QString str;
901  switch ( style )
902  {
904  str = QStringLiteral( "hidden" );
905  break;
907  str = QStringLiteral( "group" );
908  break;
910  str = QStringLiteral( "subgroup" );
911  break;
912  default:
913  break; // nothing
914  }
915 
916  if ( !str.isEmpty() )
917  node->setCustomProperty( QStringLiteral( "legend/title-style" ), str );
918  else
919  node->removeCustomProperty( QStringLiteral( "legend/title-style" ) );
920 }
921 
923 {
924  paintAndDetermineSize( context );
925 }
926 
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:97
qgsexpressioncontextutils.h
QgsLegendStyle::Symbol
@ Symbol
Symbol icon (excluding label)
Definition: qgslegendstyle.h:47
QgsLegendSettings::symbolAlignment
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
Definition: qgslegendsettings.h:291
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:41
QgsLegendRenderer::setNodeLegendStyle
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
Definition: qgslegendrenderer.cpp:898
QgsLayerTreeNode
This class is a base class for nodes in a layer tree.
Definition: qgslayertreenode.h:75
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:174
qgslayertreemodellegendnode.h
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:596
QgsLegendSettings::mmPerMapUnit
Q_DECL_DEPRECATED double mmPerMapUnit() const
Definition: qgslegendsettings.cpp:41
QgsLayerTreeModelLegendNode::ItemContext::columnRight
double columnRight
Right side of current legend column.
Definition: qgslayertreemodellegendnode.h:179
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:156
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:202
QgsLegendSettings::drawText
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
Definition: qgslegendsettings.cpp:116
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:265
QgsLayerTreeModelLegendNode::ItemContext::point
Q_DECL_DEPRECATED QPointF point
Top-left corner of the legend item.
Definition: qgslayertreemodellegendnode.h:151
QgsLegendSettings::mapScale
Q_DECL_DEPRECATED double mapScale() const
Returns the legend map scale.
Definition: qgslegendsettings.cpp:61
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
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:57
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:96
QgsLegendRenderer::nodeLegendStyle
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
Definition: qgslegendrenderer.cpp:870
QgsLayerTreeModelLegendNode::ItemContext::patchShape
QgsLegendPatchShape patchShape
The patch shape to render for the node.
Definition: qgslayertreemodellegendnode.h:193
QgsLayerTree::toLayer
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
QgsLayerTreeModel
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Definition: qgslayertreemodel.h:54
QgsLayerTreeModel::legendNodeEmbeddedInParent
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
Definition: qgslayertreemodel.cpp:1535
QgsLegendStyle::SymbolLabel
@ SymbolLabel
Symbol label (excluding icon)
Definition: qgslegendstyle.h:48
QgsLayerTreeModelLegendNode::ItemContext::labelXOffset
Q_DECL_DEPRECATED double labelXOffset
Offset from the left side where label should start.
Definition: qgslayertreemodellegendnode.h:157
QgsLayerTree::toGroup
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsLayerTreeModelLegendNode::draw
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
Definition: qgslayertreemodellegendnode.cpp:80
QgsLegendStyle::Title
@ Title
Legend title.
Definition: qgslegendstyle.h:44
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:797
QgsLayerTreeModelLegendNode::ItemMetrics::labelSize
QSizeF labelSize
Definition: qgslayertreemodellegendnode.h:208
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:456
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:42
QgsLayerTreeModelLegendNode::userPatchSize
virtual QSizeF userPatchSize() const
Returns the user (overridden) size for the legend node.
Definition: qgslayertreemodellegendnode.cpp:63
QgsLayerTreeLayer
Layer tree node points to a map layer.
Definition: qgslayertreelayer.h:44
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
Layer tree group node serves as a container for layers and further groups.
Definition: qgslayertreegroup.h:35
qgsrendercontext.h
QgsLegendSettings
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
Definition: qgslegendsettings.h:39
QgsWms::legendModel
QgsLayerTreeModel * legendModel(const QgsWmsRenderContext &context, QgsLayerTree &tree)
Definition: qgswmsgetlegendgraphics.cpp:230
QgsLegendStyle::Right
@ Right
Right side.
Definition: qgslegendstyle.h:59
QgsLayerTreeLayer::AllowSplittingLegendNodesOverMultipleColumns
@ AllowSplittingLegendNodesOverMultipleColumns
Allow splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:192
QgsLayerTreeModelLegendNode::ItemContext
Definition: qgslayertreemodellegendnode.h:137
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:207
QgsScopedRenderContextScaleToMm
Scoped object for temporary scaling of a QgsRenderContext for millimeter based rendering.
Definition: qgsrendercontext.h:1032
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:112
QgsLayerTreeModel::rootGroup
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
Definition: qgslayertreemodel.cpp:519
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:345
QgsLayerTreeModelLegendNode::ItemContext::maxSiblingSymbolWidth
double maxSiblingSymbolWidth
Largest symbol width, considering all other sibling legend components associated with the current com...
Definition: qgslayertreemodellegendnode.h:186
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:490
qgsvectorlayer.h
QgsScopedRenderContextPainterSwap
Scoped object for temporary replacement of a QgsRenderContext destination painter.
Definition: qgsrendercontext.h:976
QgsLegendStyle::Left
@ Left
Left side.
Definition: qgslegendstyle.h:58
QgsLayerTree::isLayer
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
QgsLayerTreeModelLegendNode::ItemMetrics
Definition: qgslayertreemodellegendnode.h:206
qgslegendstyle.h
QgsLayerTreeModelLegendNode::layerNode
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
Definition: qgslayertreemodellegendnode.h:70
QgsMapToPixel
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:38
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:68
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:119
QgsLayerTreeModelLegendNode::ItemContext::top
double top
Top y-position of legend item.
Definition: qgslayertreemodellegendnode.h:163
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:145
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:373
QgsLegendStyle::Hidden
@ Hidden
Special style, item is hidden including margins around.
Definition: qgslegendstyle.h:43
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:181
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:179
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:1550
QgsLayerTreeModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: qgslayertreemodel.cpp:158
QgsSymbolLegendNode
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Definition: qgslayertreemodellegendnode.h:292
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:796
QgsLegendStyle::Top
@ Top
Top side.
Definition: qgslegendstyle.h:56
QgsLayerTree::isGroup
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
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:45
QgsLegendStyle::Subgroup
@ Subgroup
Legend subgroup title.
Definition: qgslegendstyle.h:46
qgssymbol.h
QgsLayerTreeModelLegendNode::ItemContext::columnLeft
double columnLeft
Left side of current legend column.
Definition: qgslayertreemodellegendnode.h:171
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:143
QgsLegendStyle::margin
double margin(Side side)
Returns the margin (in mm) for the specified side of the component.
Definition: qgslegendstyle.h:91
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:91
QgsLayerTreeModelLegendNode
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
Definition: qgslayertreemodellegendnode.h:51