Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #39211 from jdugge/indent_legend_groups
[FEATURE] Indentation of legend groups and subgroups
  • Loading branch information
m-kuhn committed Jun 25, 2021
2 parents d34a0c3 + 9cc4070 commit 4d27183
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 138 deletions.
2 changes: 2 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitem.sip.in
Expand Up @@ -207,6 +207,8 @@ Base class for graphical items within a :py:class:`QgsLayout`.
UndoLegendWmsLegendHeight,
UndoLegendTitleSpaceBottom,
UndoLegendGroupSpace,
UndoLegendGroupIndent,
UndoLegendSubgroupIndent,
UndoLegendLayerSpace,
UndoLegendSymbolSpace,
UndoLegendIconSymbolSpace,
Expand Down
18 changes: 18 additions & 0 deletions python/core/auto_generated/qgslegendstyle.sip.in
Expand Up @@ -103,6 +103,24 @@ Sets the alignment for the legend component.
.. seealso:: :py:func:`alignment`

.. versionadded:: 3.10
%End

double indent();
%Docstring
Returns the indent (in mm) of a group or subgroup.

.. seealso:: :py:func:`indent`

.. versionadded:: 3.18
%End

void setIndent( double indent );
%Docstring
Sets the indent (in mm) of a group or subgroup.

.. seealso:: :py:func:`indent`

.. versionadded:: 3.18
%End

void writeXml( const QString &name, QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const;
Expand Down
2 changes: 2 additions & 0 deletions src/core/layout/qgslayoutitem.h
Expand Up @@ -258,6 +258,8 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
UndoLegendWmsLegendHeight, //!< Legend WMS height
UndoLegendTitleSpaceBottom, //!< Legend title space
UndoLegendGroupSpace, //!< Legend group spacing
UndoLegendGroupIndent, //!< Legend group indent
UndoLegendSubgroupIndent, //!< Legend subgroup indent
UndoLegendLayerSpace, //!< Legend layer spacing
UndoLegendSymbolSpace, //!< Legend symbol spacing
UndoLegendIconSymbolSpace, //!< Legend icon symbol space
Expand Down
64 changes: 58 additions & 6 deletions src/core/qgslegendrenderer.cpp
Expand Up @@ -270,7 +270,7 @@ void QgsLegendRenderer::widthAndOffsetForTitleText( const Qt::AlignmentFlag hali
}
}

QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponentGroupList( QgsLayerTreeGroup *parentGroup, QgsRenderContext &context )
QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponentGroupList( QgsLayerTreeGroup *parentGroup, QgsRenderContext &context, double indent )
{
QList<LegendComponentGroup> componentGroups;

Expand All @@ -283,15 +283,28 @@ QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponen
if ( QgsLayerTree::isGroup( node ) )
{
QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
QString style = node->customProperty( QStringLiteral( "legend/title-style" ) ).toString();
// Update the required indent for the group/subgroup items, starting from the indent accumulated from parent groups
double newIndent = indent;
if ( style == QLatin1String( "subgroup" ) )
{
newIndent += mSettings.style( QgsLegendStyle::Subgroup ).indent( );
}
else
{
newIndent += mSettings.style( QgsLegendStyle::Group ).indent( );
}

// Group subitems
QList<LegendComponentGroup> subgroups = createComponentGroupList( nodeGroup, context );
QList<LegendComponentGroup> subgroups = createComponentGroupList( nodeGroup, context, newIndent );

bool hasSubItems = !subgroups.empty();

if ( nodeLegendStyle( nodeGroup ) != QgsLegendStyle::Hidden )
{
LegendComponent component;
component.item = node;
component.indent = newIndent;
component.size = drawGroupTitle( nodeGroup, context );

if ( !subgroups.isEmpty() )
Expand Down Expand Up @@ -350,6 +363,7 @@ QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponen
LegendComponent component;
component.item = node;
component.size = drawLayerTitle( nodeLayer, context );
component.indent = indent;
group.components.append( component );
group.size.rwidth() = component.size.width();
group.size.rheight() = component.size.height();
Expand Down Expand Up @@ -400,6 +414,7 @@ QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponen
group.size.rheight() += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
}
group.size.rheight() += symbolComponent.size.height();
symbolComponent.indent = indent;
group.components.append( symbolComponent );
}
else
Expand All @@ -415,6 +430,7 @@ QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponen
}
LegendComponentGroup symbolGroup;
symbolGroup.placeColumnBreakBeforeGroup = forceBreak;
symbolComponent.indent = indent;
symbolGroup.components.append( symbolComponent );
symbolGroup.size.rwidth() = symbolComponent.size.width();
symbolGroup.size.rheight() = symbolComponent.size.height();
Expand Down Expand Up @@ -661,7 +677,27 @@ QSizeF QgsLegendRenderer::drawGroup( const LegendComponentGroup &group, QgsRende
currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
}
QSizeF groupSize;
groupSize = drawGroupTitle( groupItem, context, columnContext, currentY );
ColumnContext columnContextForItem = columnContext;
double indentWidth = component.indent;
if ( s == QgsLegendStyle::Subgroup )
{
// Remove indent - the subgroup items should be indented, not the subgroup title
indentWidth -= mSettings.style( QgsLegendStyle::Subgroup ).indent( );
}
else
{
// Remove indent - the group items should be indented, not the group title
indentWidth -= mSettings.style( QgsLegendStyle::Group ).indent( );
}
if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignLeft )
{
columnContextForItem.left += indentWidth;
}
if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignRight )
{
columnContextForItem.right -= indentWidth;
}
groupSize = drawGroupTitle( groupItem, context, columnContextForItem, currentY );
size.rwidth() = std::max( groupSize.width(), size.width() );
}
}
Expand All @@ -675,7 +711,11 @@ QSizeF QgsLegendRenderer::drawGroup( const LegendComponentGroup &group, QgsRende
currentY += mSettings.style( s ).margin( QgsLegendStyle::Top );
}
QSizeF subGroupSize;
subGroupSize = drawLayerTitle( layerItem, context, columnContext, currentY );

ColumnContext columnContextForItem = columnContext;
double indentWidth = component.indent;
columnContextForItem.left += indentWidth;
subGroupSize = drawLayerTitle( layerItem, context, columnContextForItem, currentY );
size.rwidth() = std::max( subGroupSize.width(), size.width() );
}
}
Expand All @@ -686,9 +726,21 @@ QSizeF QgsLegendRenderer::drawGroup( const LegendComponentGroup &group, QgsRende
currentY += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
}

LegendComponent symbolComponent = drawSymbolItem( legendNode, context, columnContext, currentY, component.maxSiblingSymbolWidth );
ColumnContext columnContextForItem = columnContext;
double indentWidth = 0;
indentWidth = component.indent;
if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignLeft )
{
columnContextForItem.left += indentWidth;
}
if ( mSettings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignRight )
{
columnContextForItem.right -= indentWidth;
}

LegendComponent symbolComponent = drawSymbolItem( legendNode, context, columnContextForItem, currentY, component.maxSiblingSymbolWidth );
// expand width, it may be wider because of label offsets
size.rwidth() = std::max( symbolComponent.size.width(), size.width() );
size.rwidth() = std::max( symbolComponent.size.width() + indentWidth, size.width() );
}
currentY += component.size.height();
first = false;
Expand Down
8 changes: 7 additions & 1 deletion src/core/qgslegendrenderer.h
Expand Up @@ -144,6 +144,12 @@ class CORE_EXPORT QgsLegendRenderer
//! Component size
QSizeF size;

/**
* Starting indent for groups/subgroups nested in other groups/subgroups.
* This value is the sum of the indents of all parent groups/subgroups.
*/
double indent = 0;

/**
* Horizontal offset for the symbol label.
*
Expand Down Expand Up @@ -215,7 +221,7 @@ class CORE_EXPORT QgsLegendRenderer
* Returns a list of component groups for the specified \a parentGroup, respecting the current layer's
* splitting settings.
*/
QList<LegendComponentGroup> createComponentGroupList( QgsLayerTreeGroup *parentGroup, QgsRenderContext &context );
QList<LegendComponentGroup> createComponentGroupList( QgsLayerTreeGroup *parentGroup, QgsRenderContext &context, double indent = 0 );

/**
* Divides a list of component groups into columns, and sets the column index for each group in the list.
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgslegendsettings.cpp
Expand Up @@ -36,6 +36,8 @@ QgsLegendSettings::QgsLegendSettings()
rstyle( QgsLegendStyle::Group ).rfont().setPointSizeF( 14.0 );
rstyle( QgsLegendStyle::Subgroup ).rfont().setPointSizeF( 12.0 );
rstyle( QgsLegendStyle::SymbolLabel ).rfont().setPointSizeF( 12.0 );
rstyle( QgsLegendStyle::Group ).setIndent( 0.0 );
rstyle( QgsLegendStyle::Subgroup ).setIndent( 0.0 );
}

double QgsLegendSettings::mmPerMapUnit() const
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgslegendstyle.cpp
Expand Up @@ -49,6 +49,7 @@ void QgsLegendStyle::writeXml( const QString &name, QDomElement &elem, QDomDocum

styleElem.setAttribute( QStringLiteral( "name" ), name );
styleElem.setAttribute( QStringLiteral( "alignment" ), QString::number( mAlignment ) );
styleElem.setAttribute( QStringLiteral( "indent" ), QString::number( mIndent ) );

if ( !qgsDoubleNear( mMarginMap[Top], 0.0 ) )
styleElem.setAttribute( QStringLiteral( "marginTop" ), QString::number( mMarginMap[Top] ) );
Expand Down Expand Up @@ -80,6 +81,7 @@ void QgsLegendStyle::readXml( const QDomElement &elem, const QDomDocument &doc,
mMarginMap[Right] = elem.attribute( QStringLiteral( "marginRight" ), QStringLiteral( "0" ) ).toDouble();

mAlignment = static_cast< Qt::Alignment >( elem.attribute( QStringLiteral( "alignment" ), QString::number( Qt::AlignLeft ) ).toInt() );
mIndent = elem.attribute( QStringLiteral( "indent" ), QStringLiteral( "0" ) ).toDouble();
}

QString QgsLegendStyle::styleName( Style s )
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgslegendstyle.h
Expand Up @@ -121,6 +121,22 @@ class CORE_EXPORT QgsLegendStyle
*/
void setAlignment( Qt::Alignment alignment ) { mAlignment = alignment; }

/**
* Returns the indent (in mm) of a group or subgroup.
*
* \see indent()
* \since QGIS 3.18
*/
double indent() { return mIndent; }

/**
* Sets the indent (in mm) of a group or subgroup.
*
* \see indent()
* \since QGIS 3.18
*/
void setIndent( double indent ) { mIndent = indent; }

/**
* Writes the component's style definition to an XML element.
* \see readXml()
Expand Down Expand Up @@ -159,6 +175,7 @@ class CORE_EXPORT QgsLegendStyle
QFont mFont;
QMap<Side, double> mMarginMap;
Qt::Alignment mAlignment = Qt::AlignLeft;
double mIndent = 0;
};

#endif
30 changes: 30 additions & 0 deletions src/gui/layout/qgslayoutlegendwidget.cpp
Expand Up @@ -94,6 +94,8 @@ QgsLayoutLegendWidget::QgsLayoutLegendWidget( QgsLayoutItemLegend *legend, QgsMa
connect( mWmsLegendHeightSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mWmsLegendHeightSpinBox_valueChanged );
connect( mTitleSpaceBottomSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mTitleSpaceBottomSpinBox_valueChanged );
connect( mGroupSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mGroupSpaceSpinBox_valueChanged );
connect( mGroupIndentSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mGroupIndentSpinBox_valueChanged );
connect( mSubgroupIndentSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mSubgroupIndentSpinBox_valueChanged );
connect( mSpaceBelowGroupHeadingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceBelowGroupHeadingChanged );
connect( mGroupSideSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceGroupSideChanged );
connect( mLayerSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mLayerSpaceSpinBox_valueChanged );
Expand Down Expand Up @@ -240,6 +242,8 @@ void QgsLayoutLegendWidget::setGuiElements()
mWmsLegendHeightSpinBox->setValue( mLegend->wmsLegendHeight() );
mTitleSpaceBottomSpinBox->setValue( mLegend->style( QgsLegendStyle::Title ).margin( QgsLegendStyle::Bottom ) );
mGroupSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).margin( QgsLegendStyle::Top ) );
mGroupIndentSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).indent() );
mSubgroupIndentSpinBox->setValue( mLegend->style( QgsLegendStyle::Subgroup ).indent() );
mGroupSideSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).margin( QgsLegendStyle::Left ) );
mSpaceBelowGroupHeadingSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).margin( QgsLegendStyle::Bottom ) );
mLayerSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::Subgroup ).margin( QgsLegendStyle::Top ) );
Expand Down Expand Up @@ -493,6 +497,30 @@ void QgsLayoutLegendWidget::mGroupSpaceSpinBox_valueChanged( double d )
}
}

void QgsLayoutLegendWidget::mGroupIndentSpinBox_valueChanged( double d )
{
if ( mLegend )
{
mLegend->beginCommand( tr( "Change Group Indent" ), QgsLayoutItem::UndoLegendGroupIndent );
mLegend->rstyle( QgsLegendStyle::Group ).setIndent( d );
mLegend->adjustBoxSize();
mLegend->update();
mLegend->endCommand();
}
}

void QgsLayoutLegendWidget::mSubgroupIndentSpinBox_valueChanged( double d )
{
if ( mLegend )
{
mLegend->beginCommand( tr( "Change Subgroup Indent" ), QgsLayoutItem::UndoLegendSubgroupIndent );
mLegend->rstyle( QgsLegendStyle::Subgroup ).setIndent( d );
mLegend->adjustBoxSize();
mLegend->update();
mLegend->endCommand();
}
}

void QgsLayoutLegendWidget::spaceBelowGroupHeadingChanged( double space )
{
if ( mLegend )
Expand Down Expand Up @@ -1258,6 +1286,8 @@ void QgsLayoutLegendWidget::blockAllSignals( bool b )
mMaxSymbolSizeSpinBox->blockSignals( b );
mMinSymbolSizeSpinBox->blockSignals( b );
mGroupSpaceSpinBox->blockSignals( b );
mGroupIndentSpinBox->blockSignals( b );
mSubgroupIndentSpinBox->blockSignals( b );
mSpaceBelowGroupHeadingSpinBox->blockSignals( b );
mGroupSideSpinBox->blockSignals( b );
mSpaceBelowSubgroupHeadingSpinBox->blockSignals( b );
Expand Down
2 changes: 2 additions & 0 deletions src/gui/layout/qgslayoutlegendwidget.h
Expand Up @@ -82,6 +82,8 @@ class GUI_EXPORT QgsLayoutLegendWidget: public QgsLayoutItemBaseWidget, private
void mWmsLegendHeightSpinBox_valueChanged( double d );
void mTitleSpaceBottomSpinBox_valueChanged( double d );
void mGroupSpaceSpinBox_valueChanged( double d );
void mGroupIndentSpinBox_valueChanged( double d );
void mSubgroupIndentSpinBox_valueChanged( double d );
void mLayerSpaceSpinBox_valueChanged( double d );
void mSymbolSpaceSpinBox_valueChanged( double d );
void mIconLabelSpaceSpinBox_valueChanged( double d );
Expand Down

0 comments on commit 4d27183

Please sign in to comment.