33  : mLegendModel( legendModel )
 
   35  , mSettings( settings )
 
   37  mProxyModel->setLayerTreeModel( mLegendModel );
 
 
   41  : mLegendModel( other.mLegendModel )
 
   42  , mProxyModel( std::move( other.mProxyModel ) )
 
   43  , mSettings( std::move( other.mSettings ) )
 
   44  , mLegendSize( other.mLegendSize )
 
   46  mProxyModel->setLayerTreeModel( mLegendModel );
 
 
   53  std::unique_ptr< QgsRenderContext > tmpContext;
 
   60    tmpContext->setRendererScale( mSettings.
mapScale() );
 
   63    renderContext = tmpContext.get();
 
   68  return paintAndDetermineSize( *renderContext );
 
 
   77  context.setRendererScale( mSettings.
mapScale() );
 
   81  paintAndDetermineSize( context );
 
 
   93  json[QStringLiteral( 
"title" )] = mSettings.
title();
 
 
  101  const QList<QgsLayerTreeNode *> childNodes = nodeGroup->
children();
 
  104    if ( !mProxyModel->nodeShown( node ) )
 
  110      const QModelIndex idx = mLegendModel->
node2index( nodeGroup );
 
  111      const QString text = mLegendModel->
data( idx, Qt::DisplayRole ).toString();
 
  114      group[ QStringLiteral( 
"type" ) ] = QStringLiteral( 
"group" );
 
  115      group[ QStringLiteral( 
"title" ) ] = text;
 
  116      nodes.append( group );
 
  125        const QModelIndex idx = mLegendModel->
node2index( nodeLayer );
 
  126        text = mLegendModel->
data( idx, Qt::DisplayRole ).toString();
 
  129      QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->
layerLegendNodes( nodeLayer );
 
  134      if ( legendNodes.count() == 1 )
 
  136        QJsonObject group = legendNodes.at( 0 )->exportToJson( mSettings, context );
 
  137        group[ QStringLiteral( 
"type" ) ] = QStringLiteral( 
"layer" );
 
  142            if ( vLayer->renderer() )
 
  145              if ( ! ruleKey.isEmpty() )
 
  148                const QString ruleExp { vLayer->renderer()->legendKeyToExpression( ruleKey, vLayer, ok ) };
 
  151                  group[ QStringLiteral( 
"rule" ) ] = ruleExp;
 
  157        nodes.append( group );
 
  159      else if ( legendNodes.count() > 1 )
 
  162        group[ QStringLiteral( 
"type" ) ] = QStringLiteral( 
"layer" );
 
  163        group[ QStringLiteral( 
"title" ) ] = text;
 
  166        for ( 
int j = 0; j < legendNodes.count(); j++ )
 
  174              if ( vLayer->renderer() )
 
  177                if ( ! ruleKey.isEmpty() )
 
  180                  const QString ruleExp { vLayer->renderer()->legendKeyToExpression( ruleKey, vLayer, ok ) };
 
  183                    symbol[ QStringLiteral( 
"rule" ) ] = ruleExp;
 
  189          symbols.append( symbol );
 
  191        group[ QStringLiteral( 
"symbols" ) ] = symbols;
 
  193        nodes.append( group );
 
  198  json[QStringLiteral( 
"nodes" )] = nodes;
 
  215  QList<LegendComponentGroup> componentGroups = createComponentGroupList( rootGroup, context );
 
  217  const int columnCount = setColumns( componentGroups );
 
  219  QMap< int, double > maxColumnWidths;
 
  220  qreal maxEqualColumnWidth = 0;
 
  228  for ( 
const LegendComponentGroup &group : std::as_const( componentGroups ) )
 
  230    const QSizeF actualSize = drawGroup( group, context, ColumnContext() );
 
  231    maxEqualColumnWidth = std::max( actualSize.width(), maxEqualColumnWidth );
 
  232    maxColumnWidths[ group.column ] = std::max( actualSize.width(), maxColumnWidths.value( group.column, 0 ) );
 
  235  if ( columnCount == 1 )
 
  238    maxEqualColumnWidth = std::max( maxEqualColumnWidth, mLegendSize.width() - 2 * mSettings.
boxSpace() );
 
  239    maxColumnWidths[ 0 ] = maxEqualColumnWidth;
 
  243  QSizeF titleSize = drawTitle( context, 0 );
 
  245  titleSize.rwidth() += mSettings.
boxSpace() * 2.0;
 
  250  bool firstInColumn = 
true;
 
  251  double columnMaxHeight = 0;
 
  252  qreal columnWidth = 0;
 
  254  ColumnContext columnContext;
 
  255  columnContext.left = mSettings.
boxSpace();
 
  256  columnContext.right = std::max( mLegendSize.width() - mSettings.
boxSpace(), mSettings.
boxSpace() );
 
  257  double currentY = columnTop;
 
  259  for ( 
const LegendComponentGroup &group : std::as_const( componentGroups ) )
 
  261    if ( group.column > column )
 
  264      columnContext.left = group.column > 0 ? ( columnContext.right + mSettings.
columnSpace() ) : mSettings.boxSpace();
 
  265      columnWidth = mSettings.
equalColumnWidth() ? maxEqualColumnWidth : maxColumnWidths.value( group.column );
 
  266      columnContext.right = columnContext.left + columnWidth;
 
  267      currentY = columnTop;
 
  269      firstInColumn = 
true;
 
  271    if ( !firstInColumn )
 
  273      currentY += spaceAboveGroup( group );
 
  276    drawGroup( group, context, columnContext, currentY );
 
  278    currentY += group.size.height();
 
  279    columnMaxHeight = std::max( currentY - columnTop, columnMaxHeight );
 
  281    firstInColumn = 
false;
 
  283  const double totalWidth = columnContext.right + mSettings.
boxSpace();
 
  285  size.rheight() = columnTop + columnMaxHeight + mSettings.
boxSpace();
 
  286  size.rwidth() = totalWidth;
 
  287  if ( !mSettings.
title().isEmpty() )
 
  289    size.rwidth() = std::max( titleSize.width(), size.width() );
 
  293  if ( mLegendSize.isValid() )
 
  295    qreal w = std::max( size.width(), mLegendSize.width() );
 
  296    qreal h = std::max( size.height(), mLegendSize.height() );
 
  297    size = QSizeF( w, h );
 
  301  if ( !mSettings.
title().isEmpty() )
 
  309void QgsLegendRenderer::widthAndOffsetForTitleText( 
const Qt::AlignmentFlag halignment, 
const double legendWidth, 
double &textBoxWidth, 
double &textBoxLeft )
 const 
  311  switch ( halignment )
 
  315      textBoxWidth = legendWidth - 2 * mSettings.
boxSpace();
 
  318    case Qt::AlignHCenter:
 
  321      const double centerX = legendWidth / 2;
 
  322      textBoxWidth = ( std::min( 
static_cast< double >( centerX ), legendWidth - centerX ) - mSettings.
boxSpace() ) * 2.0;
 
  323      textBoxLeft = centerX - textBoxWidth / 2.;
 
  329QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponentGroupList( 
QgsLayerTreeGroup *parentGroup, 
QgsRenderContext &context, 
double indent )
 
  331  QList<LegendComponentGroup> componentGroups;
 
  334    return componentGroups;
 
  336  const QList<QgsLayerTreeNode *> childNodes = parentGroup->
children();
 
  339    if ( !mProxyModel->nodeShown( node ) )
 
  345      QString style = node->customProperty( QStringLiteral( 
"legend/title-style" ) ).toString();
 
  347      double newIndent = indent;
 
  348      if ( style == QLatin1String( 
"subgroup" ) )
 
  358      QList<LegendComponentGroup> subgroups = createComponentGroupList( nodeGroup, context, newIndent );
 
  360      bool hasSubItems = !subgroups.empty();
 
  364        LegendComponent component;
 
  365        component.item = node;
 
  366        component.indent = newIndent;
 
  367        component.size = drawGroupTitle( nodeGroup, context );
 
  369        if ( !subgroups.isEmpty() )
 
  372          subgroups[0].size.rheight() += spaceAboveGroup( subgroups[0] );
 
  374          subgroups[0].components.prepend( component );
 
  375          subgroups[0].size.rheight() += component.size.height();
 
  376          subgroups[0].size.rwidth() = std::max( component.size.width(), subgroups[0].size.width() );
 
  377          if ( nodeGroup->
customProperty( QStringLiteral( 
"legend/column-break" ) ).toInt() )
 
  378            subgroups[0].placeColumnBreakBeforeGroup = 
true;
 
  383          LegendComponentGroup group;
 
  384          group.placeColumnBreakBeforeGroup = nodeGroup->
customProperty( QStringLiteral( 
"legend/column-break" ) ).toInt();
 
  385          group.components.append( component );
 
  386          group.size.rwidth() += component.size.width();
 
  387          group.size.rheight() += component.size.height();
 
  388          group.size.rwidth() = std::max( component.size.width(), group.size.width() );
 
  389          subgroups.append( group );
 
  395        componentGroups.append( subgroups );
 
  403      bool allowColumnSplit = 
false;
 
  410          allowColumnSplit = 
true;
 
  413          allowColumnSplit = 
false;
 
  417      LegendComponentGroup group;
 
  418      group.placeColumnBreakBeforeGroup = nodeLayer->
customProperty( QStringLiteral( 
"legend/column-break" ) ).toInt();
 
  422        LegendComponent component;
 
  423        component.item = node;
 
  424        component.size = drawLayerTitle( nodeLayer, context );
 
  425        component.indent = indent;
 
  426        group.components.append( component );
 
  427        group.size.rwidth() = component.size.width();
 
  428        group.size.rheight() = component.size.height();
 
  431      QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->
layerLegendNodes( nodeLayer );
 
  439      QList<LegendComponentGroup> layerGroups;
 
  440      layerGroups.reserve( legendNodes.count() );
 
  442      bool groupIsLayerGroup = 
true;
 
  443      double symbolIndent = indent;
 
  444      switch ( layerStyle )
 
  448          symbolIndent += mSettings.
style( layerStyle ).
indent( );
 
  453      for ( 
int j = 0; j < legendNodes.count(); j++ )
 
  457        LegendComponent symbolComponent = drawSymbolItem( legendNode, context, ColumnContext(), 0 );
 
  461        if ( !allowColumnSplit || j == 0 )
 
  465            if ( groupIsLayerGroup )
 
  466              layerGroups.prepend( group );
 
  468              layerGroups.append( group );
 
  470            group = LegendComponentGroup();
 
  471            group.placeColumnBreakBeforeGroup = 
true;
 
  472            groupIsLayerGroup = 
false;
 
  477          group.size.rwidth() = std::max( symbolComponent.size.width(), group.size.width() );
 
  479          if ( !group.components.isEmpty() )
 
  484          group.size.rheight() += symbolComponent.size.height();
 
  485          symbolComponent.indent = symbolIndent;
 
  486          group.components.append( symbolComponent );
 
  490          if ( group.size.height() > 0 )
 
  492            if ( groupIsLayerGroup )
 
  493              layerGroups.prepend( group );
 
  495              layerGroups.append( group );
 
  496            group = LegendComponentGroup();
 
  497            groupIsLayerGroup = 
false;
 
  499          LegendComponentGroup symbolGroup;
 
  500          symbolGroup.placeColumnBreakBeforeGroup = forceBreak;
 
  501          symbolComponent.indent = symbolIndent;
 
  502          symbolGroup.components.append( symbolComponent );
 
  503          symbolGroup.size.rwidth() = symbolComponent.size.width();
 
  504          symbolGroup.size.rheight() = symbolComponent.size.height();
 
  505          layerGroups.append( symbolGroup );
 
  508      if ( group.size.height() > 0 )
 
  510        if ( groupIsLayerGroup )
 
  511          layerGroups.prepend( group );
 
  513          layerGroups.append( group );
 
  515      componentGroups.append( layerGroups );
 
  519  return componentGroups;
 
  523int QgsLegendRenderer::setColumns( QList<LegendComponentGroup> &componentGroups )
 
  526  double totalHeight = 0;
 
  527  qreal maxGroupHeight = 0;
 
  528  int forcedColumnBreaks = 0;
 
  529  double totalSpaceAboveGroups = 0;
 
  531  for ( 
const LegendComponentGroup &group : std::as_const( componentGroups ) )
 
  533    const double topMargin = spaceAboveGroup( group );
 
  534    totalHeight += topMargin;
 
  535    totalSpaceAboveGroups += topMargin;
 
  537    const double groupHeight = group.size.height();
 
  538    totalHeight += groupHeight;
 
  539    maxGroupHeight = std::max( groupHeight, maxGroupHeight );
 
  541    if ( group.placeColumnBreakBeforeGroup )
 
  542      forcedColumnBreaks++;
 
  544  const double totalGroupHeight = ( totalHeight - totalSpaceAboveGroups );
 
  545  double averageGroupHeight = totalGroupHeight / componentGroups.size();
 
  547  if ( mSettings.
columnCount() == 0 && forcedColumnBreaks == 0 )
 
  552  const int targetNumberColumns = std::max( forcedColumnBreaks + 1, mSettings.
columnCount() );
 
  553  const int numberAutoPlacedBreaks = targetNumberColumns - forcedColumnBreaks - 1;
 
  560  double maxColumnHeight = 0;
 
  561  int currentColumn = 0;
 
  562  int currentColumnGroupCount = 0; 
 
  563  double currentColumnHeight = 0;
 
  564  int autoPlacedBreaks = 0;
 
  567  double averageSpaceAboveGroups = 0;
 
  568  if ( componentGroups.size() > targetNumberColumns )
 
  569    averageSpaceAboveGroups = totalSpaceAboveGroups / ( componentGroups.size() );
 
  571  double totalRemainingGroupHeight = totalGroupHeight;
 
  572  double totalRemainingSpaceAboveGroups = totalSpaceAboveGroups;
 
  573  for ( 
int i = 0; i < componentGroups.size(); i++ )
 
  575    const LegendComponentGroup &group = componentGroups.at( i );
 
  576    const double currentGroupHeight = group.size.height();
 
  577    const double spaceAboveCurrentGroup = spaceAboveGroup( group );
 
  579    totalRemainingGroupHeight -= currentGroupHeight;
 
  580    totalRemainingSpaceAboveGroups -= spaceAboveCurrentGroup;
 
  582    double currentColumnHeightIfGroupIsIncluded = currentColumnHeight;
 
  583    if ( currentColumnGroupCount > 0 )
 
  584      currentColumnHeightIfGroupIsIncluded += spaceAboveCurrentGroup;
 
  585    currentColumnHeightIfGroupIsIncluded += currentGroupHeight;
 
  587    const int numberRemainingGroupsIncludingThisOne = componentGroups.size() - i;
 
  588    const int numberRemainingColumnsIncludingThisOne = numberAutoPlacedBreaks + 1 - autoPlacedBreaks;
 
  589    const int numberRemainingColumnBreaks = numberRemainingColumnsIncludingThisOne - 1;
 
  591    const double averageRemainingSpaceAboveGroups = numberRemainingGroupsIncludingThisOne > 1 ? ( totalRemainingSpaceAboveGroups / ( numberRemainingGroupsIncludingThisOne - 1 ) ) : 0;
 
  592    const double estimatedRemainingSpaceAboveGroupsWhichWontBeUsedBecauseGroupsAreFirstInColumn = numberRemainingColumnBreaks * averageRemainingSpaceAboveGroups;
 
  593    const double estimatedRemainingTotalHeightAfterThisGroup = totalRemainingGroupHeight
 
  594        + totalRemainingSpaceAboveGroups
 
  595        - estimatedRemainingSpaceAboveGroupsWhichWontBeUsedBecauseGroupsAreFirstInColumn;
 
  597    const double estimatedTotalHeightOfRemainingColumnsIncludingThisOne = currentColumnHeightIfGroupIsIncluded
 
  598        + estimatedRemainingTotalHeightAfterThisGroup;
 
  601    double averageRemainingColumnHeightIncludingThisOne = estimatedTotalHeightOfRemainingColumnsIncludingThisOne / numberRemainingColumnsIncludingThisOne;
 
  605    const int averageGroupsPerRemainingColumnsIncludingThisOne = std::ceil( averageRemainingColumnHeightIncludingThisOne / ( averageGroupHeight + averageSpaceAboveGroups ) );
 
  607    averageRemainingColumnHeightIncludingThisOne = averageGroupsPerRemainingColumnsIncludingThisOne * ( averageGroupHeight + averageSpaceAboveGroups ) - averageSpaceAboveGroups;
 
  609    bool canCreateNewColumn = ( currentColumnGroupCount > 0 )  
 
  610                              && ( currentColumn < targetNumberColumns - 1 ) 
 
  611                              && ( autoPlacedBreaks < numberAutoPlacedBreaks );
 
  613    bool shouldCreateNewColumn = currentColumnHeightIfGroupIsIncluded > averageRemainingColumnHeightIncludingThisOne  
 
  614                                 && currentColumnGroupCount > 0 
 
  615                                 && currentColumnHeightIfGroupIsIncluded > maxGroupHeight  
 
  616                                 && currentColumnHeightIfGroupIsIncluded > maxColumnHeight; 
 
  618    shouldCreateNewColumn |= group.placeColumnBreakBeforeGroup;
 
  619    canCreateNewColumn |= group.placeColumnBreakBeforeGroup;
 
  623    shouldCreateNewColumn |= ( componentGroups.size() - i < targetNumberColumns - currentColumn );
 
  625    if ( canCreateNewColumn && shouldCreateNewColumn )
 
  629      if ( !group.placeColumnBreakBeforeGroup )
 
  631      currentColumnGroupCount = 0;
 
  632      currentColumnHeight = group.size.height();
 
  636      currentColumnHeight = currentColumnHeightIfGroupIsIncluded;
 
  638    componentGroups[i].column = currentColumn;
 
  639    currentColumnGroupCount++;
 
  640    maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
 
  643  auto refineColumns = [&componentGroups, 
this]() -> 
bool 
  645    QHash< int, double > columnHeights;
 
  646    QHash< int, int > columnGroupCounts;
 
  647    double currentColumnHeight = 0;
 
  648    int currentColumn = -1;
 
  651    double maxCurrentColumnHeight = 0;
 
  652    for ( 
int i = 0; i < componentGroups.size(); i++ )
 
  654      const LegendComponentGroup &group = componentGroups.at( i );
 
  655      if ( group.column != currentColumn )
 
  657        if ( currentColumn >= 0 )
 
  659          columnHeights.insert( currentColumn, currentColumnHeight );
 
  660          columnGroupCounts.insert( currentColumn, groupCount );
 
  663        currentColumn = group.column;
 
  664        currentColumnHeight = 0;
 
  666        columnCount = std::max( columnCount, currentColumn + 1 );
 
  669      const double spaceAbove = spaceAboveGroup( group );
 
  670      currentColumnHeight += spaceAbove + group.size.height();
 
  673    columnHeights.insert( currentColumn, currentColumnHeight );
 
  674    columnGroupCounts.insert( currentColumn, groupCount );
 
  676    double totalColumnHeights = 0;
 
  677    for ( 
int i = 0; i < columnCount; ++ i )
 
  679      totalColumnHeights += columnHeights[i];
 
  680      maxCurrentColumnHeight = std::max( maxCurrentColumnHeight, columnHeights[i] );
 
  683    const double averageColumnHeight = totalColumnHeights / columnCount;
 
  685    bool changed = 
false;
 
  686    int nextCandidateColumnForShift = 1;
 
  687    for ( 
int i = 0; i < componentGroups.size(); i++ )
 
  689      LegendComponentGroup &group = componentGroups[ i ];
 
  690      if ( group.column < nextCandidateColumnForShift )
 
  694      const bool canShift = !group.placeColumnBreakBeforeGroup
 
  695                            && columnGroupCounts[ group.column ] >= 2;
 
  698           && columnHeights[ group.column - 1 ] < averageColumnHeight
 
  699           && ( columnHeights[ group.column - 1 ] + group.size.height() ) * 0.9 < maxCurrentColumnHeight
 
  703        columnHeights[ group.column ] += group.size.height() + spaceAboveGroup( group );
 
  704        columnGroupCounts[ group.column ]++;
 
  705        columnHeights[ group.column + 1 ] -= group.size.height();
 
  706        columnGroupCounts[ group.column + 1]--;
 
  711        nextCandidateColumnForShift = group.column + 1;
 
  717  bool wasRefined = 
true;
 
  719  while ( wasRefined && iterations < 2 )
 
  721    wasRefined = refineColumns();
 
  726  QMap<QString, qreal> maxSymbolWidth;
 
  727  for ( 
int i = 0; i < componentGroups.size(); i++ )
 
  729    LegendComponentGroup &group = componentGroups[i];
 
  730    for ( 
int j = 0; j < group.components.size(); j++ )
 
  734        QString key = QStringLiteral( 
"%1-%2" ).arg( 
reinterpret_cast< qulonglong 
>( 
legendNode->
layerNode() ) ).arg( group.column );
 
  735        maxSymbolWidth[key] = std::max( group.components.at( j ).symbolSize.width(), maxSymbolWidth[key] );
 
  739  for ( 
int i = 0; i < componentGroups.size(); i++ )
 
  741    LegendComponentGroup &group = componentGroups[i];
 
  742    for ( 
int j = 0; j < group.components.size(); j++ )
 
  746        QString key = QStringLiteral( 
"%1-%2" ).arg( 
reinterpret_cast< qulonglong 
>( 
legendNode->
layerNode() ) ).arg( group.column );
 
  749        group.components[j].labelXOffset = maxSymbolWidth[key] + space;
 
  750        group.components[j].maxSiblingSymbolWidth = maxSymbolWidth[key];
 
  751        group.components[j].size.rwidth() = maxSymbolWidth[key] + space + group.components.at( j ).labelSize.width();
 
  755  return targetNumberColumns;
 
  758QSizeF QgsLegendRenderer::drawTitle( 
QgsRenderContext &context, 
double top, Qt::AlignmentFlag halignment, 
double legendWidth )
 const 
  761  if ( mSettings.
title().isEmpty() )
 
  771  widthAndOffsetForTitleText( halignment, legendWidth, textBoxWidth, textBoxLeft );
 
  776  double overallTextHeight = 0;
 
  777  double overallTextWidth = 0;
 
  785  size.rheight() = overallTextHeight / dotsPerMM;
 
  786  size.rwidth() = overallTextWidth / dotsPerMM;
 
  792    const QRectF r( textBoxLeft * dotsPerMM, top * dotsPerMM, textBoxWidth * dotsPerMM, overallTextHeight );
 
  804double QgsLegendRenderer::spaceAboveGroup( 
const LegendComponentGroup &group )
 
  806  if ( group.components.isEmpty() ) 
return 0;
 
  808  LegendComponent component = group.components.first();
 
  810  if ( 
QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
 
  814  else if ( 
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
 
  818  else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( component.item ) )
 
  827QSizeF QgsLegendRenderer::drawGroup( 
const LegendComponentGroup &group, 
QgsRenderContext &context, ColumnContext columnContext, 
double top )
 
  830  QSizeF size = QSizeF( group.size );
 
  831  double currentY = top;
 
  832  for ( 
const LegendComponent &component : std::as_const( group.components ) )
 
  834    if ( 
QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( component.item ) )
 
  844        ColumnContext columnContextForItem = columnContext;
 
  845        double indentWidth =  component.indent;
 
  858          columnContextForItem.left += indentWidth;
 
  862          columnContextForItem.right -= indentWidth;
 
  864        groupSize = drawGroupTitle( groupItem, context, columnContextForItem, currentY );
 
  865        size.rwidth() = std::max( groupSize.width(), size.width() );
 
  868    else if ( 
QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( component.item ) )
 
  879        ColumnContext columnContextForItem = columnContext;
 
  880        double indentWidth =  component.indent;
 
  881        columnContextForItem.left += indentWidth;
 
  882        subGroupSize = drawLayerTitle( layerItem, context, columnContextForItem, currentY );
 
  883        size.rwidth() = std::max( subGroupSize.width(), size.width() );
 
  893      ColumnContext columnContextForItem = columnContext;
 
  894      double indentWidth = 0;
 
  895      indentWidth = component.indent;
 
  898        columnContextForItem.left += indentWidth;
 
  902        columnContextForItem.right -= indentWidth;
 
  905      LegendComponent symbolComponent = drawSymbolItem( legendNode, context, columnContextForItem, currentY, component.maxSiblingSymbolWidth );
 
  907      size.rwidth() = std::max( symbolComponent.size.width() + indentWidth, size.width() );
 
  909    currentY += component.size.height();
 
  930  ctx.
point = QPointF( columnContext.left, top );
 
  956    symbolScope = symbolNode->createSymbolScope();
 
  971  LegendComponent component;
 
  972  component.item = symbolItem;
 
  979  double width = std::max( 
static_cast< double >( im.
symbolSize.width() ), maxSiblingSymbolWidth )
 
  986  component.size = QSizeF( width, height );
 
  993  QModelIndex idx = mLegendModel->
node2index( nodeLayer );
 
  994  QString titleString = mLegendModel->
data( idx, Qt::DisplayRole ).toString();
 
  996  if ( titleString.isEmpty() )
 
 1002  if ( nodeLayer->
layer() )
 
 1012  double overallTextHeight = 0;
 
 1013  double overallTextWidth = 0;
 
 1021  size.rheight() = overallTextHeight / dotsPerMM;
 
 1022  size.rwidth() = overallTextWidth / dotsPerMM + sideMargin *
 
 1032                    ( ( columnContext.right - columnContext.left ) - ( halign == 
Qgis::TextHorizontalAlignment::
Right ? sideMargin : 0 ) ) * dotsPerMM, overallTextHeight );
 
 1046  QSizeF size( 0, 0 );
 
 1047  QModelIndex idx = mLegendModel->
node2index( nodeGroup );
 
 1053  double overallTextHeight = 0;
 
 1054  double overallTextWidth = 0;
 
 1065  size.rheight() = overallTextHeight / dotsPerMM;
 
 1066  size.rwidth() = overallTextWidth / dotsPerMM + sideMargin *
 
 1077                    dotsPerMM * ( ( columnContext.right - columnContext.left ) - ( halign == 
Qgis::TextHorizontalAlignment::
Right ? sideMargin : 0 ) ), overallTextHeight );
 
 1087  QString style = node->
customProperty( QStringLiteral( 
"legend/title-style" ) ).toString();
 
 1088  if ( style == QLatin1String( 
"hidden" ) )
 
 1090  else if ( style == QLatin1String( 
"group" ) )
 
 1092  else if ( style == QLatin1String( 
"subgroup" ) )
 
 
 1115  return mProxyModel.get();
 
 
 1124      str = QStringLiteral( 
"hidden" );
 
 1127      str = QStringLiteral( 
"group" );
 
 1130      str = QStringLiteral( 
"subgroup" );
 
 1136  if ( !str.isEmpty() )
 
 
 1144  paintAndDetermineSize( context );
 
 
Provides global constants and enumerations for use throughout the application.
 
LegendComponent
Component of legends which can be styled.
 
@ Symbol
Symbol icon (excluding label)
 
@ Group
Legend group title.
 
@ Hidden
Special style, item is hidden including margins around.
 
@ Subgroup
Legend subgroup title.
 
@ SymbolLabel
Symbol label (excluding icon)
 
@ Undefined
Should not happen, only if corrupted project file.
 
@ RectangleAscentBased
Similar to Rectangle mode, but uses ascents only when calculating font and line heights.
 
@ Rectangle
Text within rectangle layout mode.
 
@ ShowRuleDetails
If set, the rule expression of a rule based renderer legend item will be added to the JSON.
 
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
 
TextHorizontalAlignment
Text horizontal alignment.
 
Single scope for storing variables and functions for use within a QgsExpressionContext.
 
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
 
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
 
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
 
A sort filter proxy model to easily reproduce the legend/layer tree in a tree view.
 
Layer tree group node serves as a container for layers and further groups.
 
Layer tree node points to a map layer.
 
@ AllowSplittingLegendNodesOverMultipleColumns
Allow splitting node's legend nodes across multiple columns.
 
@ PreventSplittingLegendNodesOverMultipleColumns
Prevent splitting node's legend nodes across multiple columns.
 
@ UseDefaultLegendSetting
Inherit default legend column splitting setting.
 
LegendNodesSplitBehavior legendSplitBehavior() const
Returns the column split behavior for the node.
 
QgsMapLayer * layer() const
Returns the map layer associated with this node.
 
An abstract interface for legend items returned from QgsMapLayerLegend implementation.
 
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
 
QJsonObject exportToJson(const QgsLegendSettings &settings, const QgsRenderContext &context)
Entry point called from QgsLegendRenderer to do the rendering in a JSON object.
 
virtual bool columnBreak() const
Returns whether a forced column break should occur before the node.
 
@ RuleKey
Rule key of the node (QString)
 
virtual QSizeF userPatchSize() const
Returns the user (overridden) size for the legend node.
 
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
 
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
 
A model representing the layer tree, including layers and groups of layers.
 
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...
 
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...
 
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
 
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
 
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
 
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
 
Base class for nodes in a layer tree.
 
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.
 
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
 
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
 
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
 
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
 
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
 
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
 
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
 
Handles automatic layout and rendering of legends.
 
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
 
QgsLayerTreeFilterProxyModel * proxyModel()
Returns the filter proxy model used for filtering the legend model content during rendering.
 
QJsonObject exportLegendToJson(const QgsRenderContext &context)
Renders the legend in a json object.
 
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Constructor for QgsLegendRenderer.
 
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
 
static void setNodeLegendStyle(QgsLayerTreeNode *node, Qgis::LegendComponent style)
Sets the style of a node.
 
static Qgis::LegendComponent nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
 
Stores the appearance and layout settings for legend drawing with QgsLegendRenderer.
 
int columnCount() const
Returns the desired minimum number of columns to show in the legend.
 
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
 
QString title() const
Returns the title for the legend, which will be rendered above all legend items.
 
double columnSpace() const
Returns the margin space between adjacent columns (in millimeters).
 
void updateDataDefinedProperties(QgsRenderContext &context)
Updates any data-defined properties in the settings, using the specified render context.
 
QgsLegendStyle style(Qgis::LegendComponent s) const
Returns the style for a legend component.
 
double boxSpace() const
Returns the legend box space (in millimeters), which is the empty margin around the inside of the leg...
 
Q_DECL_DEPRECATED double mmPerMapUnit() const
 
bool splitLayer() const
Returns true if layer components can be split over multiple columns.
 
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...
 
Qgis::LegendJsonRenderFlags jsonRenderFlags() const
Returns the JSON export flags.
 
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
 
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
 
bool equalColumnWidth() const
Returns true if all columns should have equal widths.
 
Q_DECL_DEPRECATED double mapScale() const
Returns the legend map scale.
 
Qt::Alignment alignment() const
Returns the alignment for the legend component.
 
QgsTextFormat & textFormat()
Returns the text format used for rendering this legend component.
 
double margin(Side side) const
Returns the margin (in mm) for the specified side of the component.
 
double indent() const
Returns the indent (in mm) of a group or subgroup.
 
Perform transforms between map coordinates and device coordinates.
 
Contains information about the context of a rendering operation.
 
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
 
QPainter * painter()
Returns the destination QPainter for the render operation.
 
QgsExpressionContext & expressionContext()
Gets the expression context.
 
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
 
Scoped object for temporary replacement of a QgsRenderContext destination painter.
 
Scoped object for temporary scaling of a QgsRenderContext for millimeter based rendering.
 
Scoped object for temporary scaling of a QgsRenderContext for pixel based rendering.
 
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
 
Container for all settings relating to text rendering.
 
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
 
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
 
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
 
Represents a vector layer which manages a vector based dataset.
 
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
 
#define Q_NOWARN_DEPRECATED_POP
 
#define Q_NOWARN_DEPRECATED_PUSH
 
QPainter * painter
Painter.
 
double top
Top y-position of legend item.
 
Q_DECL_DEPRECATED double labelXOffset
Offset from the left side where label should start.
 
QgsLegendPatchShape patchShape
The patch shape to render for the node.
 
double maxSiblingSymbolWidth
Largest symbol width, considering all other sibling legend components associated with the current com...
 
QSizeF patchSize
Symbol patch size to render for the node.
 
double columnLeft
Left side of current legend column.
 
double columnRight
Right side of current legend column.
 
Q_DECL_DEPRECATED QPointF point
Top-left corner of the legend item.
 
Q_NOWARN_DEPRECATED_POP QgsRenderContext * context
Render context, if available.