Skip to content

Commit

Permalink
adding QgsRenderContext support
Browse files Browse the repository at this point in the history
  • Loading branch information
roya0045 authored and nyalldawson committed Jan 10, 2019
1 parent abb0f73 commit 99837cf
Showing 1 changed file with 311 additions and 1 deletion.
312 changes: 311 additions & 1 deletion src/core/qgslegendrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ QgsLegendRenderer::QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsL

QSizeF QgsLegendRenderer::minimumSize()
{
return paintAndDetermineSize( nullptr );
return paintAndDetermineSize();
}

void QgsLegendRenderer::drawLegend( QPainter *painter )
Expand Down Expand Up @@ -629,3 +629,313 @@ void QgsLegendRenderer::setNodeLegendStyle( QgsLayerTreeNode *node, QgsLegendSty
else
node->removeCustomProperty( QStringLiteral( "legend/title-style" ) );
}

void QgsLegendRenderer::drawLegend( QPainter *painter )
{
paintAndDetermineSize( painter );
}


//new version of classes using QgsRenderContext
QSizeF QgsLegendRenderer::drawTitle( QgsRenderContext *rendercontext, QPointF point, Qt::AlignmentFlag halignment, double legendWidth )
{
QSizeF size( 0, 0 );
if ( mSettings.title().isEmpty() )
{
return size;
}

QStringList lines = mSettings.splitStringForWrapping( mSettings.title() );
double y = point.y();

if ( rendercontext->painter() )
{
rendercontext->painter()->setPen( mSettings.fontColor() );
}

//calculate width and left pos of rectangle to draw text into
double textBoxWidth;
double textBoxLeft;
switch ( halignment )
{
case Qt::AlignHCenter:
textBoxWidth = ( std::min( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.boxSpace() ) * 2.0;
textBoxLeft = point.x() - textBoxWidth / 2.;
break;
case Qt::AlignRight:
textBoxLeft = mSettings.boxSpace();
textBoxWidth = point.x() - mSettings.boxSpace();
break;
case Qt::AlignLeft:
default:
textBoxLeft = point.x();
textBoxWidth = legendWidth - point.x() - mSettings.boxSpace();
break;
}

QFont titleFont = mSettings.style( QgsLegendStyle::Title ).font();

for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
{
//last word is not drawn if rectangle width is exactly text width, so add 1
//TODO - correctly calculate size of italicized text, since QFontMetrics does not
qreal width = mSettings.textWidthMillimeters( titleFont, *titlePart ) + 1;
qreal height = mSettings.fontAscentMillimeters( titleFont ) + mSettings.fontDescentMillimeters( titleFont );

QRectF r( textBoxLeft, y, textBoxWidth, height );

if ( rendercontext->painter() )
{
mSettings.drawText( rendercontext->painter(), r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
}

//update max width of title
size.rwidth() = std::max( width, size.rwidth() );

y += height;
if ( titlePart != ( lines.end() - 1 ) )
{
y += mSettings.lineSpacing();
}
}
size.rheight() = y - point.y();

return size;
}



// Draw atom and expand its size (using actual nucleons labelXOffset)
QSizeF QgsLegendRenderer::drawAtom( const Atom &atom, QgsRenderContext *rendercontext, QPointF point )
{
bool first = true;
QSizeF size = QSizeF( atom.size );
Q_FOREACH ( const Nucleon &nucleon, atom.nucleons )
{
if ( QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
{
QgsLegendStyle::Style s = nodeLegendStyle( groupItem );
if ( s != QgsLegendStyle::Hidden )
{
if ( !first )
{
point.ry() += mSettings.style( s ).margin( QgsLegendStyle::Top );
}
drawGroupTitle( groupItem, rendercontext, point );
}
}
else if ( QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
{
QgsLegendStyle::Style s = nodeLegendStyle( layerItem );
if ( s != QgsLegendStyle::Hidden )
{
if ( !first )
{
point.ry() += mSettings.style( s ).margin( QgsLegendStyle::Top );
}
drawLayerTitle( layerItem, rendercontext, point );
}
}
else if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( nucleon.item ) )
{
if ( !first )
{
point.ry() += mSettings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top );
}

Nucleon symbolNucleon = drawSymbolItem( legendNode, rendercontext, point, nucleon.labelXOffset );
// expand width, it may be wider because of labelXOffset
size.rwidth() = std::max( symbolNucleon.size.width(), size.width() );
}
point.ry() += nucleon.size.height();
first = false;
}
return size;
}


QgsLegendRenderer::Nucleon QgsLegendRenderer::drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, QgsRenderContext *rendercontext, QPointF point, double labelXOffset )
{
QgsLayerTreeModelLegendNode::ItemContext ctx;
ctx.painter=rendercontext->painter();
ctx.point = point;
ctx.labelXOffset = labelXOffset;

QgsLayerTreeModelLegendNode::ItemMetrics im = symbolItem->draw( mSettings, rendercontext ? &ctx : nullptr );

Nucleon nucleon;
nucleon.item = symbolItem;
nucleon.symbolSize = im.symbolSize;
nucleon.labelSize = im.labelSize;
//QgsDebugMsg( QStringLiteral( "symbol height = %1 label height = %2").arg( symbolSize.height()).arg( labelSize.height() ));
double width = std::max( static_cast< double >( im.symbolSize.width() ), labelXOffset ) + im.labelSize.width();
double height = std::max( im.symbolSize.height(), im.labelSize.height() );
nucleon.size = QSizeF( width, height );
return nucleon;
}


QSizeF QgsLegendRenderer::drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QgsRenderContext *rendercontext, QPointF point )
{
QSizeF size( 0, 0 );
QModelIndex idx = mLegendModel->node2index( nodeLayer );

//Let the user omit the layer title item by having an empty layer title string
if ( mLegendModel->data( idx, Qt::DisplayRole ).toString().isEmpty() ) return size;

double y = point.y();

if ( rendercontext->painter() ) rendercontext->painter()->setPen( mSettings.fontColor() );

QFont layerFont = mSettings.style( nodeLegendStyle( nodeLayer ) ).font();

QStringList lines = mSettings.splitStringForWrapping( mLegendModel->data( idx, Qt::DisplayRole ).toString() );
for ( QStringList::Iterator layerItemPart = lines.begin(); layerItemPart != lines.end(); ++layerItemPart )
{
y += mSettings.fontAscentMillimeters( layerFont );
if ( rendercontext->painter() ) mSettings.drawText( rendercontext->painter(), point.x(), y, *layerItemPart, layerFont );
qreal width = mSettings.textWidthMillimeters( layerFont, *layerItemPart );
size.rwidth() = std::max( width, size.width() );
if ( layerItemPart != ( lines.end() - 1 ) )
{
y += mSettings.lineSpacing();
}
}
size.rheight() = y - point.y();

return size;
}


QSizeF QgsLegendRenderer::drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QgsRenderContext *rendercontext, QPointF point )
{
QSizeF size( 0, 0 );
QModelIndex idx = mLegendModel->node2index( nodeGroup );

double y = point.y();

if ( rendercontext->painter() ) rendercontext->painter()->setPen( mSettings.fontColor() );

QFont groupFont = mSettings.style( nodeLegendStyle( nodeGroup ) ).font();

QStringList lines = mSettings.splitStringForWrapping( mLegendModel->data( idx, Qt::DisplayRole ).toString() );
for ( QStringList::Iterator groupPart = lines.begin(); groupPart != lines.end(); ++groupPart )
{
y += mSettings.fontAscentMillimeters( groupFont );
if ( rendercontext->painter() ) mSettings.drawText( rendercontext->painter(), point.x(), y, *groupPart, groupFont );
qreal width = mSettings.textWidthMillimeters( groupFont, *groupPart );
size.rwidth() = std::max( width, size.width() );
if ( groupPart != ( lines.end() - 1 ) )
{
y += mSettings.lineSpacing();
}
}
size.rheight() = y - point.y();
return size;
}


void QgsLegendRenderer::drawLegend( QgsRenderContext *rendercontext )
{
paintAndDetermineSize( rendercontext );
}

QSizeF QgsLegendRenderer::paintAndDetermineSize( QgsRenderContext *rendercontext )
{
QSizeF size( 0, 0 );
QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
if ( !rootGroup ) return size;

QList<Atom> atomList = createAtomList( rootGroup, mSettings.splitLayer() );

setColumns( atomList );

qreal maxColumnWidth = 0;
if ( mSettings.equalColumnWidth() )
{
Q_FOREACH ( const Atom &atom, atomList )
{
maxColumnWidth = std::max( atom.size.width(), maxColumnWidth );
}
}

//calculate size of title
QSizeF titleSize = drawTitle();
//add title margin to size of title text
titleSize.rwidth() += mSettings.boxSpace() * 2.0;
double columnTop = mSettings.boxSpace() + titleSize.height() + mSettings.style( QgsLegendStyle::Title ).margin( QgsLegendStyle::Bottom );

QPointF point( mSettings.boxSpace(), columnTop );
bool firstInColumn = true;
double columnMaxHeight = 0;
qreal columnWidth = 0;
int column = 0;
Q_FOREACH ( const Atom &atom, atomList )
{
if ( atom.column > column )
{
// Switch to next column
if ( mSettings.equalColumnWidth() )
{
point.rx() += mSettings.columnSpace() + maxColumnWidth;
}
else
{
point.rx() += mSettings.columnSpace() + columnWidth;
}
point.ry() = columnTop;
columnWidth = 0;
column++;
firstInColumn = true;
}
if ( !firstInColumn )
{
point.ry() += spaceAboveAtom( atom );
}

QSizeF atomSize = drawAtom( atom, rendercontext, point );
columnWidth = std::max( atomSize.width(), columnWidth );

point.ry() += atom.size.height();
columnMaxHeight = std::max( point.y() - columnTop, columnMaxHeight );

firstInColumn = false;
}
point.rx() += columnWidth + mSettings.boxSpace();

size.rheight() = columnTop + columnMaxHeight + mSettings.boxSpace();
size.rwidth() = point.x();
if ( !mSettings.title().isEmpty() )
{
size.rwidth() = std::max( titleSize.width(), size.width() );
}

// override the size if it was set by the user
if ( mLegendSize.isValid() )
{
qreal w = std::max( size.width(), mLegendSize.width() );
qreal h = std::max( size.height(), mLegendSize.height() );
size = QSizeF( w, h );
}

// Now we have set the correct total item width and can draw the title centered
if ( !mSettings.title().isEmpty() )
{
if ( mSettings.titleAlignment() == Qt::AlignLeft )
{
point.rx() = mSettings.boxSpace();
}
else if ( mSettings.titleAlignment() == Qt::AlignHCenter )
{
point.rx() = size.width() / 2;
}
else
{
point.rx() = size.width() - mSettings.boxSpace();
}
point.ry() = mSettings.boxSpace();
drawTitle( rendercontext, point, mSettings.titleAlignment(), size.width() );
}

return size;
}

0 comments on commit 99837cf

Please sign in to comment.