[needs-docs][labeling] Fix substitutions don't play well with wrapped labels

Fixes an issue identified in the upcoming QGIS Map Design 2nd ed
(spoiler alert!) where it's impossible to utilise label text
substitutions if you also want to use word wrapping.

This isn't possible to directly fix, because we need to evaluate
the full label expression (including the word wrapping component)
in order to actually HAVE text to substitute into.

So, a new setting has been added to the label formatting tab
allowing users to directly set an auto-wrapping line ideal
line size. This is applied AFTER label text evaluation, substitutions,
and the 'wrap text on' character, so it can play correctly well with
all these other settings. This also has the nice side-effect
of making auto label text wrapping more accessible to new
users/those unfamiliar with the wordwrap expression function.

Fixes #20007, and cleans up a chapter of QMD 2ed ;)
This commit is contained in:
Nyall Dawson 2018-10-04 12:57:10 +10:00
parent dce8673d21
commit 234985b59d
10 changed files with 263 additions and 114 deletions

View File

@ -159,6 +159,7 @@ class QgsPalLayerSettings
// text formatting
MultiLineWrapChar,
AutoWrapLength,
MultiLineHeight,
MultiLineAlignment,
DirSymbDraw,
@ -283,6 +284,10 @@ Returns the QgsExpression for this label settings. May be None if isExpression i
QString wrapChar;
int autoWrapLength;
bool useMaxLineLengthForAutoWrap;
MultiLineAlign multilineAlign;
bool addDirectionSymbol;
@ -559,15 +564,16 @@ Checks whether a geometry requires preparation before registration with PAL
.. versionadded:: 2.9
%End
static QStringList splitToLines( const QString &text, const QString &wrapCharacter );
static QStringList splitToLines( const QString &text, const QString &wrapCharacter, int autoWrapLength = 0, bool useMaxLineLengthWhenAutoWrapping = true );
%Docstring
Splits a text string to a list of separate lines, using a specified wrap character.
Splits a ``text`` string to a list of separate lines, using a specified wrap character (``wrapCharacter``).
The text string will be split on either newline characters or the wrap character.
:param text: text string to split
:param wrapCharacter: additional character to wrap on
:return: list of text split to lines
Since QGIS 3.4 the ``autoWrapLength`` argument can be used to specify an ideal length of line to automatically
wrap text to (automatic wrapping is disabled if ``autoWrapLength`` is 0). This automatic wrapping is performed
after processing wrapping using ``wrapCharacter``. When auto wrapping is enabled, the ``useMaxLineLengthWhenAutoWrapping``
argument controls whether the lines should be wrapped to an ideal maximum of ``autoWrapLength`` characters, or
if false then the lines are wrapped to an ideal minimum length of ``autoWrapLength`` characters.
.. versionadded:: 2.9
%End

View File

@ -264,7 +264,7 @@ links.
static QString wordWrap( const QString &string, int length, bool useMaxLineLength = true, const QString &customDelimiter = QString() );
%Docstring
Automatically wraps a \string by inserting new line characters at appropriate locations in the string.
Automatically wraps a ``string`` by inserting new line characters at appropriate locations in the string.
The ``length`` argument specifies either the minimum or maximum length of lines desired, depending
on whether ``useMaxLineLength`` is true. If ``useMaxLineLength`` is true, then the string will be wrapped

View File

@ -247,6 +247,8 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
mMaxCharAngleOutDSpinBox->setValue( std::fabs( lyr.maxCurvedCharAngleOut ) );
wrapCharacterEdit->setText( lyr.wrapChar );
mAutoWrapLengthSpinBox->setValue( lyr.autoWrapLength );
mAutoWrapTypeComboBox->setCurrentIndex( lyr.useMaxLineLengthForAutoWrap ? 0 : 1 );
mFontMultiLineAlignComboBox->setCurrentIndex( ( unsigned int ) lyr.multilineAlign );
chkPreserveRotation->setChecked( lyr.preserveRotation );
@ -435,6 +437,8 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.fontMinPixelSize = mFontMinPixelSpinBox->value();
lyr.fontMaxPixelSize = mFontMaxPixelSpinBox->value();
lyr.wrapChar = wrapCharacterEdit->text();
lyr.autoWrapLength = mAutoWrapLengthSpinBox->value();
lyr.useMaxLineLengthForAutoWrap = mAutoWrapTypeComboBox->currentIndex() == 0;
lyr.multilineAlign = ( QgsPalLayerSettings::MultiLineAlign ) mFontMultiLineAlignComboBox->currentIndex();
lyr.preserveRotation = chkPreserveRotation->isChecked();
@ -465,6 +469,7 @@ void QgsLabelingGui::populateDataDefinedButtons()
// text formatting
registerDataDefinedButton( mWrapCharDDBtn, QgsPalLayerSettings::MultiLineWrapChar );
registerDataDefinedButton( mAutoWrapLengthDDBtn, QgsPalLayerSettings::AutoWrapLength );
registerDataDefinedButton( mFontLineHeightDDBtn, QgsPalLayerSettings::MultiLineHeight );
registerDataDefinedButton( mFontMultiLineAlignDDBtn, QgsPalLayerSettings::MultiLineAlignment );

View File

@ -124,6 +124,7 @@ void QgsPalLayerSettings::initPropertyDefinitions()
{ QgsPalLayerSettings::FontWordSpacing, QgsPropertyDefinition( "FontWordSpacing", QObject::tr( "Word spacing" ), QgsPropertyDefinition::Double, origin ) },
{ QgsPalLayerSettings::FontBlendMode, QgsPropertyDefinition( "FontBlendMode", QObject::tr( "Text blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
{ QgsPalLayerSettings::MultiLineWrapChar, QgsPropertyDefinition( "MultiLineWrapChar", QObject::tr( "Wrap character" ), QgsPropertyDefinition::String, origin ) },
{ QgsPalLayerSettings::AutoWrapLength, QgsPropertyDefinition( "AutoWrapLength", QObject::tr( "Automatic word wrap line length" ), QgsPropertyDefinition::IntegerPositive, origin ) },
{ QgsPalLayerSettings::MultiLineHeight, QgsPropertyDefinition( "MultiLineHeight", QObject::tr( "Line height" ), QgsPropertyDefinition::DoublePositive, origin ) },
{ QgsPalLayerSettings::MultiLineAlignment, QgsPropertyDefinition( "MultiLineAlignment", QgsPropertyDefinition::DataTypeString, QObject::tr( "Line alignment" ), QObject::tr( "string " ) + "[<b>Left</b>|<b>Center</b>|<b>Right</b>|<b>Follow</b>]", origin ) },
{ QgsPalLayerSettings::DirSymbDraw, QgsPropertyDefinition( "DirSymbDraw", QObject::tr( "Draw direction symbol" ), QgsPropertyDefinition::Boolean, origin ) },
@ -321,6 +322,8 @@ QgsPalLayerSettings &QgsPalLayerSettings::operator=( const QgsPalLayerSettings &
// text formatting
wrapChar = s.wrapChar;
autoWrapLength = s.autoWrapLength;
useMaxLineLengthForAutoWrap = s.useMaxLineLengthForAutoWrap;
multilineAlign = s.multilineAlign;
addDirectionSymbol = s.addDirectionSymbol;
leftDirectionSymbol = s.leftDirectionSymbol;
@ -532,6 +535,9 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer )
// text formatting
wrapChar = layer->customProperty( QStringLiteral( "labeling/wrapChar" ) ).toString();
autoWrapLength = layer->customProperty( QStringLiteral( "labeling/autoWrapLength" ) ).toInt();
useMaxLineLengthForAutoWrap = layer->customProperty( QStringLiteral( "labeling/useMaxLineLengthForAutoWrap" ), QStringLiteral( "1" ) ).toBool();
multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( QStringLiteral( "labeling/multilineAlign" ), QVariant( MultiFollowPlacement ) ).toUInt() );
addDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/addDirectionSymbol" ) ).toBool();
leftDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/leftDirectionSymbol" ), QVariant( "<" ) ).toString();
@ -739,6 +745,8 @@ void QgsPalLayerSettings::readXml( QDomElement &elem, const QgsReadWriteContext
// text formatting
QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
wrapChar = textFormatElem.attribute( QStringLiteral( "wrapChar" ) );
autoWrapLength = textFormatElem.attribute( QStringLiteral( "autoWrapLength" ), QStringLiteral( "0" ) ).toInt();
useMaxLineLengthForAutoWrap = textFormatElem.attribute( QStringLiteral( "useMaxLineLengthForAutoWrap" ), QStringLiteral( "1" ) ).toInt();
multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( QStringLiteral( "multilineAlign" ), QString::number( MultiFollowPlacement ) ).toUInt() );
addDirectionSymbol = textFormatElem.attribute( QStringLiteral( "addDirectionSymbol" ) ).toInt();
leftDirectionSymbol = textFormatElem.attribute( QStringLiteral( "leftDirectionSymbol" ), QStringLiteral( "<" ) );
@ -953,6 +961,8 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite
// text formatting
QDomElement textFormatElem = doc.createElement( QStringLiteral( "text-format" ) );
textFormatElem.setAttribute( QStringLiteral( "wrapChar" ), wrapChar );
textFormatElem.setAttribute( QStringLiteral( "autoWrapLength" ), autoWrapLength );
textFormatElem.setAttribute( QStringLiteral( "useMaxLineLengthForAutoWrap" ), useMaxLineLengthForAutoWrap );
textFormatElem.setAttribute( QStringLiteral( "multilineAlign" ), static_cast< unsigned int >( multilineAlign ) );
textFormatElem.setAttribute( QStringLiteral( "addDirectionSymbol" ), addDirectionSymbol );
textFormatElem.setAttribute( QStringLiteral( "leftDirectionSymbol" ), leftDirectionSymbol );
@ -1046,6 +1056,7 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString t
QgsRenderContext *rc = context ? context : scopedRc.get();
QString wrapchr = wrapChar;
int evalAutoWrapLength = autoWrapLength;
double multilineH = mFormat.lineHeight();
bool addDirSymb = addDirectionSymbol;
@ -1060,6 +1071,11 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString t
wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
}
if ( dataDefinedValues.contains( QgsPalLayerSettings::AutoWrapLength ) )
{
evalAutoWrapLength = dataDefinedValues.value( QgsPalLayerSettings::AutoWrapLength, evalAutoWrapLength ).toInt();
}
if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
{
multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
@ -1095,6 +1111,9 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString t
rc->expressionContext().setOriginalValueVariable( wrapChar );
wrapchr = mDataDefinedProperties.value( QgsPalLayerSettings::MultiLineWrapChar, rc->expressionContext(), wrapchr ).toString();
rc->expressionContext().setOriginalValueVariable( evalAutoWrapLength );
evalAutoWrapLength = mDataDefinedProperties.value( QgsPalLayerSettings::AutoWrapLength, rc->expressionContext(), evalAutoWrapLength ).toInt();
rc->expressionContext().setOriginalValueVariable( multilineH );
multilineH = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MultiLineHeight, rc->expressionContext(), multilineH );
@ -1140,7 +1159,7 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString t
}
double w = 0.0, h = 0.0;
QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr, evalAutoWrapLength, useMaxLineLengthForAutoWrap );
int lines = multiLineSplit.size();
double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
@ -2422,6 +2441,12 @@ void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
wrapchr = exprVal.toString();
}
int evalAutoWrapLength = autoWrapLength;
if ( dataDefinedValEval( DDInt, QgsPalLayerSettings::AutoWrapLength, exprVal, context.expressionContext(), evalAutoWrapLength ) )
{
evalAutoWrapLength = exprVal.toInt();
}
// data defined multiline height?
dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
@ -2844,13 +2869,14 @@ bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry &geometry, Q
return false;
}
QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter, const int autoWrapLength, const bool useMaxLineLengthWhenAutoWrapping )
{
QStringList multiLineSplit;
if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
{
//wrap on both the wrapchr and new line characters
Q_FOREACH ( const QString &line, text.split( wrapCharacter ) )
const QStringList lines = text.split( wrapCharacter );
for ( const QString &line : lines )
{
multiLineSplit.append( line.split( '\n' ) );
}
@ -2860,6 +2886,17 @@ QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wr
multiLineSplit = text.split( '\n' );
}
// apply auto wrapping to each manually created line
if ( autoWrapLength != 0 )
{
QStringList autoWrappedLines;
autoWrappedLines.reserve( multiLineSplit.count() );
for ( const QString &line : qgis::as_const( multiLineSplit ) )
{
autoWrappedLines.append( QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split( '\n' ) );
}
multiLineSplit = autoWrappedLines;
}
return multiLineSplit;
}
@ -3044,7 +3081,12 @@ void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings &tmpLyr,
tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
}
if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( QLatin1String( "wordwrap" ) ) )
if ( ddValues.contains( QgsPalLayerSettings::AutoWrapLength ) )
{
tmpLyr.autoWrapLength = ddValues.value( QgsPalLayerSettings::AutoWrapLength ).toInt();
}
if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( QLatin1String( "wordwrap" ) ) || tmpLyr.autoWrapLength > 0 )
{
if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )

View File

@ -269,6 +269,7 @@ class CORE_EXPORT QgsPalLayerSettings
// text formatting
MultiLineWrapChar = 31,
AutoWrapLength = 101,
MultiLineHeight = 32,
MultiLineAlignment = 33,
DirSymbDraw = 34,
@ -417,6 +418,27 @@ class CORE_EXPORT QgsPalLayerSettings
*/
QString wrapChar;
/**
* If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified
* number of characters. If zero, auto wrapping is disabled.
*
* \see useMaxLineLengthForAutoWrap
* \since QGIS 3.4
*/
int autoWrapLength = 0;
/**
* If true, indicates that when auto wrapping label text the autoWrapLength length indicates the maximum
* ideal length of text lines. If false, then autoWrapLength indicates the ideal minimum length of text
* lines.
*
* If autoWrapLength is 0 then this value has no effect.
*
* \see autoWrapLength
* \since QGIS 3.4
*/
bool useMaxLineLengthForAutoWrap = true;
//! Horizontal alignment of multi-line labels.
MultiLineAlign multilineAlign;
@ -986,14 +1008,18 @@ class CORE_EXPORT QgsPalLabeling
static bool geometryRequiresPreparation( const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry = QgsGeometry() );
/**
* Splits a text string to a list of separate lines, using a specified wrap character.
* Splits a \a text string to a list of separate lines, using a specified wrap character (\a wrapCharacter).
* The text string will be split on either newline characters or the wrap character.
* \param text text string to split
* \param wrapCharacter additional character to wrap on
* \returns list of text split to lines
*
* Since QGIS 3.4 the \a autoWrapLength argument can be used to specify an ideal length of line to automatically
* wrap text to (automatic wrapping is disabled if \a autoWrapLength is 0). This automatic wrapping is performed
* after processing wrapping using \a wrapCharacter. When auto wrapping is enabled, the \a useMaxLineLengthWhenAutoWrapping
* argument controls whether the lines should be wrapped to an ideal maximum of \a autoWrapLength characters, or
* if false then the lines are wrapped to an ideal minimum length of \a autoWrapLength characters.
*
* \since QGIS 2.9
*/
static QStringList splitToLines( const QString &text, const QString &wrapCharacter );
static QStringList splitToLines( const QString &text, const QString &wrapCharacter, int autoWrapLength = 0, bool useMaxLineLengthWhenAutoWrapping = true );
/**
* Splits a text string to a list of graphemes, which are the smallest allowable character

View File

@ -262,7 +262,7 @@ class CORE_EXPORT QgsStringUtils
static QString insertLinks( const QString &string, bool *foundLinks = nullptr );
/**
* Automatically wraps a \string by inserting new line characters at appropriate locations in the string.
* Automatically wraps a \a string by inserting new line characters at appropriate locations in the string.
*
* The \a length argument specifies either the minimum or maximum length of lines desired, depending
* on whether \a useMaxLineLength is true. If \a useMaxLineLength is true, then the string will be wrapped

View File

@ -612,7 +612,7 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
}
//QgsDebugMsgLevel( "drawLabel " + txt, 4 );
QStringList multiLineList = QgsPalLabeling::splitToLines( txt, tmpLyr.wrapChar );
QStringList multiLineList = QgsPalLabeling::splitToLines( txt, tmpLyr.wrapChar, tmpLyr.autoWrapLength, tmpLyr.useMaxLineLengthForAutoWrap );
QgsTextRenderer::HAlignment hAlign = QgsTextRenderer::AlignLeft;
if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiCenter )

View File

@ -456,10 +456,13 @@ void QgsTextFormatWidget::initWidget()
<< mShapeTypeDDBtn
<< mShowLabelDDBtn
<< mWrapCharDDBtn
<< mAutoWrapLengthDDBtn
<< mZIndexDDBtn
<< mZIndexSpinBox
<< spinBufferSize
<< wrapCharacterEdit
<< mAutoWrapLengthSpinBox
<< mAutoWrapTypeComboBox
<< mCentroidRadioVisible
<< mCentroidRadioWhole
<< mDirectSymbRadioBtnAbove

View File

@ -172,7 +172,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<width>486</width>
<height>300</height>
</rect>
</property>
@ -651,7 +651,7 @@
<item>
<widget class="QStackedWidget" name="mLabelStackedWidget">
<property name="currentIndex">
<number>6</number>
<number>1</number>
</property>
<widget class="QWidget" name="mLabelPage_Text">
<layout class="QVBoxLayout" name="verticalLayout_6">
@ -680,8 +680,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>444</height>
<width>375</width>
<height>470</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -1460,8 +1460,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>389</height>
<width>452</width>
<height>479</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
@ -1507,27 +1507,10 @@ font-style: italic;</string>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QgsPropertyOverrideButton" name="mWrapCharDDBtn">
<item row="1" column="0">
<widget class="QLabel" name="label_44">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="wrapCharacterEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QgsPropertyOverrideButton" name="mFontLineHeightDDBtn">
<property name="text">
<string>…</string>
<string>Wrap lines to</string>
</property>
</widget>
</item>
@ -1544,7 +1527,7 @@ font-style: italic;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="3" column="0">
<widget class="QLabel" name="mFontLineHeightLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
@ -1557,7 +1540,98 @@ font-style: italic;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QgsPropertyOverrideButton" name="mAutoWrapLengthDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="mFontMultiLineAlignComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Paragraph style alignment of multi-line text</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="4" column="2">
<widget class="QgsPropertyOverrideButton" name="mFontMultiLineAlignDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsSpinBox" name="mAutoWrapLengthSpinBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>If set, label text will automatically be wrapped to match the specified number of characters per line (if possible)</string>
</property>
<property name="specialValueText">
<string>No automatic wrapping</string>
</property>
<property name="suffix">
<string> characters</string>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="wrapCharacterEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mFontMultiLineLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Alignment</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QgsPropertyOverrideButton" name="mFontLineHeightDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QgsDoubleSpinBox" name="mFontLineHeightSpinBox">
<property name="enabled">
<bool>true</bool>
@ -1588,52 +1662,28 @@ font-style: italic;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mFontMultiLineLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="0" column="2">
<widget class="QgsPropertyOverrideButton" name="mWrapCharDDBtn">
<property name="text">
<string>Alignment</string>
<string>…</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="mFontMultiLineAlignComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<widget class="QComboBox" name="mAutoWrapTypeComboBox">
<property name="toolTip">
<string>Paragraph style alignment of multi-line text</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<string>Controls whether lines are automatically wrapped using the maximum number of characters in a line, or the minimum</string>
</property>
<item>
<property name="text">
<string>Left</string>
<string>Maximum line length</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
<string>Minimum line length</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="2" column="2">
<widget class="QgsPropertyOverrideButton" name="mFontMultiLineAlignDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
</layout>
@ -2095,8 +2145,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>464</width>
<height>366</height>
<width>466</width>
<height>365</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
@ -2441,8 +2491,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<height>738</height>
<width>464</width>
<height>776</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_21">
@ -3214,8 +3264,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>441</height>
<width>452</width>
<height>470</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
@ -3675,8 +3725,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>917</height>
<width>452</width>
<height>977</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
@ -5309,8 +5359,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>795</height>
<width>452</width>
<height>877</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
@ -6187,6 +6237,34 @@ font-style: italic;</string>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsColorButton</class>
<extends>QToolButton</extends>
<header>qgscolorbutton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsPropertyOverrideButton</class>
<extends>QToolButton</extends>
<header>qgspropertyoverridebutton.h</header>
</customwidget>
<customwidget>
<class>QgsUnitSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsunitselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsOpacityWidget</class>
<extends>QWidget</extends>
<header>qgsopacitywidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsScrollArea</class>
<extends>QScrollArea</extends>
@ -6209,28 +6287,6 @@ font-style: italic;</string>
<extends>QWidget</extends>
<header>qgsscalewidget.h</header>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsUnitSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsunitselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsColorButton</class>
<extends>QToolButton</extends>
<header>qgscolorbutton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsPropertyOverrideButton</class>
<extends>QToolButton</extends>
<header>qgspropertyoverridebutton.h</header>
</customwidget>
<customwidget>
<class>QgsBlendModeComboBox</class>
<extends>QComboBox</extends>
@ -6258,12 +6314,6 @@ font-style: italic;</string>
<header>effects/qgseffectstackpropertieswidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsOpacityWidget</class>
<extends>QWidget</extends>
<header>qgsopacitywidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mFieldExpressionWidget</tabstop>
@ -6307,6 +6357,9 @@ font-style: italic;</string>
<tabstop>scrollArea_5</tabstop>
<tabstop>wrapCharacterEdit</tabstop>
<tabstop>mWrapCharDDBtn</tabstop>
<tabstop>mAutoWrapLengthSpinBox</tabstop>
<tabstop>mAutoWrapLengthDDBtn</tabstop>
<tabstop>mAutoWrapTypeComboBox</tabstop>
<tabstop>mFontLineHeightSpinBox</tabstop>
<tabstop>mFontLineHeightDDBtn</tabstop>
<tabstop>mFontMultiLineAlignComboBox</tabstop>
@ -6536,6 +6589,12 @@ font-style: italic;</string>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>

View File

@ -61,6 +61,14 @@ void TestQgsPalLabeling::wrapChar()
QCOMPARE( QgsPalLabeling::splitToLines( "mixed new line\nand char", QString( " " ) ), QStringList() << "mixed" << "new" << "line" << "and" << "char" );
QCOMPARE( QgsPalLabeling::splitToLines( "no matching chars", QString( "#" ) ), QStringList() << "no matching chars" );
QCOMPARE( QgsPalLabeling::splitToLines( "no\nmatching\nchars", QString( "#" ) ), QStringList() << "no" << "matching" << "chars" );
// with auto wrap
QCOMPARE( QgsPalLabeling::splitToLines( "with auto wrap", QString(), 12, true ), QStringList() << "with auto" << "wrap" );
QCOMPARE( QgsPalLabeling::splitToLines( "with auto wrap", QString(), 6, false ), QStringList() << "with auto" << "wrap" );
// manual wrap character should take precedence
QCOMPARE( QgsPalLabeling::splitToLines( QStringLiteral( "with auto-wrap and manual-wrap" ), QStringLiteral( "-" ), 12, true ), QStringList() << "with auto" << "wrap and" << "manual" << "wrap" );
QCOMPARE( QgsPalLabeling::splitToLines( QStringLiteral( "with auto-wrap and manual-wrap" ), QStringLiteral( "-" ), 6, false ), QStringList() << "with auto" << "wrap and" << "manual" << "wrap" );
}
void TestQgsPalLabeling::graphemes()