Skip to content
Permalink
Browse files

[FEATURE] Title label decoration

  • Loading branch information
nirvn committed Nov 15, 2018
1 parent 3eabce5 commit 95f69c5de349b8993128cba40c7cb6d83ea5272e
@@ -109,6 +109,8 @@ SET(QGIS_APP_SRCS
qgsmaptooltextannotation.cpp

decorations/qgsdecorationitem.cpp
decorations/qgsdecorationtitle.cpp
decorations/qgsdecorationtitledialog.cpp
decorations/qgsdecorationcopyright.cpp
decorations/qgsdecorationcopyrightdialog.cpp
decorations/qgsdecorationlayoutextent.cpp
@@ -363,6 +365,8 @@ SET (QGIS_APP_MOC_HDRS
qgsmaptoolregularpolygoncentercorner.h

decorations/qgsdecorationitem.h
decorations/qgsdecorationtitle.h
decorations/qgsdecorationtitledialog.h
decorations/qgsdecorationcopyright.h
decorations/qgsdecorationcopyrightdialog.h
decorations/qgsdecorationlayoutextent.h
@@ -0,0 +1,207 @@
/***************************************************************************
qgsdecorationtitle.cpp
--------------------------------------
Date : November 2018
Copyright : (C) 2018 by Mathieu Pellerin
Email : nirvn dot asia at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsdecorationtitle.h"
#include "qgsdecorationtitledialog.h"

#include "qgisapp.h"
#include "qgsapplication.h"
#include "qgsexpression.h"
#include "qgsexpressioncontext.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsreadwritecontext.h"
#include "qgssymbollayerutils.h"

#include <QPainter>
#include <QMenu>
#include <QDate>
#include <QDomDocument>
#include <QMatrix>
#include <QFile>

//non qt includes
#include <cmath>


QgsDecorationTitle::QgsDecorationTitle( QObject *parent )
: QgsDecorationItem( parent )
{
mPlacement = TopLeft;
mMarginUnit = QgsUnitTypes::RenderMillimeters;

setName( "Title Label" );
// initialize default values in the gui
projectRead();
}

void QgsDecorationTitle::projectRead()
{
QgsDecorationItem::projectRead();

mLabelText = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Label" ), QString() );
mBackgroundColor = QgsSymbolLayerUtils::decodeColor( QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/BackgroundColor" ), QStringLiteral( "0,0,0,66" ) ) );

mMarginHorizontal = QgsProject::instance()->readNumEntry( mNameConfig, QStringLiteral( "/MarginH" ), 0 );
mMarginVertical = QgsProject::instance()->readNumEntry( mNameConfig, QStringLiteral( "/MarginV" ), 0 );

QDomDocument doc;
QDomElement elem;
QString textXml = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Font" ) );
if ( !textXml.isEmpty() )
{
doc.setContent( textXml );
elem = doc.documentElement();
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
mTextFormat.readXml( elem, rwContext );
}
}

void QgsDecorationTitle::saveToProject()
{
QgsDecorationItem::saveToProject();

QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Label" ), mLabelText );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/BackgroundColor" ), QgsSymbolLayerUtils::encodeColor( mBackgroundColor ) );

QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/MarginH" ), mMarginHorizontal );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/MarginV" ), mMarginVertical );

QDomDocument textDoc;
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
QDomElement textElem = mTextFormat.writeXml( textDoc, rwContext );
textDoc.appendChild( textElem );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Font" ), textDoc.toString() );
}

// Slot called when the buffer menu item is activated
void QgsDecorationTitle::run()
{
QgsDecorationTitleDialog dlg( *this, QgisApp::instance() );
dlg.exec();
}


void QgsDecorationTitle::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
Q_UNUSED( mapSettings );
if ( !enabled() )
return;

context.painter()->save();
context.painter()->setRenderHint( QPainter::Antialiasing, true );

QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() );
QStringList displayStringList = displayString.split( QStringLiteral( "\n" ) );

QFontMetricsF fm( mTextFormat.scaledFont( context ) );
QFontMetricsF textMetrics = QgsTextRenderer::fontMetrics( context, mTextFormat );
double textDescent = textMetrics.descent();
double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList, &fm );
double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point, &fm );

QPaintDevice *device = context.painter()->device();
int deviceHeight = device->height() / device->devicePixelRatioF();
int deviceWidth = device->width() / device->devicePixelRatioF();

float xOffset( 0 ), yOffset( 0 );

// Set margin according to selected units
switch ( mMarginUnit )
{
case QgsUnitTypes::RenderMillimeters:
{
int pixelsInchX = context.painter()->device()->logicalDpiX();
int pixelsInchY = context.painter()->device()->logicalDpiY();
xOffset = pixelsInchX * INCHES_TO_MM * mMarginHorizontal;
yOffset = pixelsInchY * INCHES_TO_MM * mMarginVertical;
break;
}
case QgsUnitTypes::RenderPixels:
{
xOffset = mMarginHorizontal;
yOffset = mMarginVertical;
break;
}
case QgsUnitTypes::RenderPercentage:
{
xOffset = ( ( deviceWidth - textWidth ) / 100. ) * mMarginHorizontal;
yOffset = ( ( deviceHeight - textHeight ) / 100. ) * mMarginVertical;
break;
}
case QgsUnitTypes::RenderMapUnits:
case QgsUnitTypes::RenderPoints:
case QgsUnitTypes::RenderInches:
case QgsUnitTypes::RenderUnknownUnit:
case QgsUnitTypes::RenderMetersInMapUnits:
break;
}

QPolygonF backgroundBar;

// Determine placement of label from form combo box
QgsTextRenderer::HAlignment horizontalAlignment = QgsTextRenderer::AlignLeft;
switch ( mPlacement )
{
case BottomLeft: // Bottom Left, xOffset is set above
backgroundBar << QPointF( 0, deviceHeight )
<< QPointF( deviceWidth, deviceHeight )
<< QPointF( deviceWidth, deviceHeight - ( yOffset * 2 + textHeight ) )
<< QPointF( 0, deviceHeight - ( yOffset * 2 + textHeight ) );
yOffset = deviceHeight - yOffset - textDescent;
break;
case TopLeft: // Top left, xOffset is set above
backgroundBar << QPointF( 0, 0 )
<< QPointF( deviceWidth, 0 )
<< QPointF( deviceWidth, yOffset * 2 + textHeight )
<< QPointF( 0, yOffset * 2 + textHeight );
yOffset = yOffset + textHeight - textDescent;
break;
case TopRight: // Top Right
backgroundBar << QPointF( 0, 0 )
<< QPointF( deviceWidth, 0 )
<< QPointF( deviceWidth, yOffset * 2 + textHeight )
<< QPointF( 0, yOffset * 2 + textHeight );
yOffset = yOffset + textHeight - textDescent;
xOffset = deviceWidth - xOffset;
horizontalAlignment = QgsTextRenderer::AlignRight;
break;
case BottomRight: // Bottom Right
backgroundBar << QPointF( 0, deviceHeight )
<< QPointF( deviceWidth, deviceHeight )
<< QPointF( deviceWidth, deviceHeight - ( yOffset * 2 + textHeight ) )
<< QPointF( 0, deviceHeight - ( yOffset * 2 + textHeight ) );
yOffset = deviceHeight - yOffset - textDescent;
xOffset = deviceWidth - xOffset;
horizontalAlignment = QgsTextRenderer::AlignRight;
break;
default:
QgsDebugMsg( QStringLiteral( "Unknown placement index of %1" ).arg( static_cast<int>( mPlacement ) ) );
}

// Draw background bar
context.painter()->setPen( Qt::NoPen );
context.painter()->setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
context.painter()->drawPolygon( backgroundBar );

// Paint label to canvas
QgsTextRenderer::drawText( QPointF( xOffset, yOffset ), 0.0, horizontalAlignment, displayStringList, context, mTextFormat );

context.painter()->restore();
}

@@ -0,0 +1,81 @@
/***************************************************************************
qgsdecorationtitle.h
--------------------------------------
Date : November 2018
Copyright : (C) 2018 by Mathieu Pellerin
Email : nirvn dot asia at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSDECORATIONTITLE_H
#define QGSDECORATIONTITLE_H

#include "qgis_app.h"
#include "qgsdecorationitem.h"
#include "qgstextrenderer.h"

#include <QColor>
#include <QFont>
#include <QObject>

class QgsDecorationTitleDialog;

class QAction;
class QPainter;

class APP_EXPORT QgsDecorationTitle : public QgsDecorationItem
{
Q_OBJECT
public:

//! Constructor
QgsDecorationTitle( QObject *parent = nullptr );

public slots:
//! Sets values on the gui when a project is read or the gui first loaded
void projectRead() override;
//! save values to the project
void saveToProject() override;

//! Show the dialog box
void run() override;
//! render the title label
void render( const QgsMapSettings &mapSettings, QgsRenderContext &context ) override;

/**
* Returns the text format for extent labels.
* \see setTextFormat()
* \see labelExtents()
*/
QgsTextFormat textFormat() const { return mTextFormat; }

/**
* Sets the text \a format for extent labels.
* \see textFormat()
* \see setLabelExtents()
*/
void setTextFormat( const QgsTextFormat &format ) { mTextFormat = format; }

private:
//! This is the string that will be used for the title label
QString mLabelText;

//! The background bar color
QColor mBackgroundColor;

//! enable or disable use of position percentage for placement
int mMarginHorizontal = 0;
int mMarginVertical = 0;

QgsTextFormat mTextFormat;

friend class QgsDecorationTitleDialog;
};

#endif

0 comments on commit 95f69c5

Please sign in to comment.
You can’t perform that action at this time.