From 7c1ab9b6a8e1b3d64c08a4f5067448884b068945 Mon Sep 17 00:00:00 2001
From: Yann Bodson <yann.bodson@nokia.com>
Date: Fri, 28 Jan 2011 11:58:10 +1000
Subject: Add support for line spacing in Text element.

This change adds the lineHeight and lineHeightMode properties.

Task-number: QTBUG-14296
Reviewed-by: Martin Jones
---
 src/declarative/graphicsitems/qdeclarativetext.cpp | 84 +++++++++++++++++++++-
 src/declarative/graphicsitems/qdeclarativetext_p.h | 13 ++++
 .../graphicsitems/qdeclarativetext_p_p.h           |  2 +
 src/gui/text/qtextdocumentlayout.cpp               | 17 ++++-
 src/gui/text/qtextdocumentlayout_p.h               |  5 +-
 .../qdeclarativetext/data/lineHeight.qml           | 15 ++++
 .../qdeclarativetext/tst_qdeclarativetext.cpp      | 32 +++++++++
 7 files changed, 162 insertions(+), 6 deletions(-)
 create mode 100644 tests/auto/declarative/qdeclarativetext/data/lineHeight.qml

diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp
index 5edfc31..fb1ae06 100644
--- a/src/declarative/graphicsitems/qdeclarativetext.cpp
+++ b/src/declarative/graphicsitems/qdeclarativetext.cpp
@@ -41,6 +41,7 @@
 
 #include "private/qdeclarativetext_p.h"
 #include "private/qdeclarativetext_p_p.h"
+#include <private/qtextdocumentlayout_p.h>
 #include <qdeclarativestyledtext_p.h>
 #include <qdeclarativeinfo.h>
 #include <qdeclarativepixmapcache_p.h>
@@ -83,6 +84,14 @@ private:
     static QSet<QUrl> errors;
 };
 
+class QDeclarativeTextDocumentLayout : public QTextDocumentLayout
+{
+    Q_OBJECT
+public:
+    QDeclarativeTextDocumentLayout(QTextDocument *doc);
+    void setLineHeight(qreal lineHeight, QDeclarativeText::LineHeightMode mode);
+};
+
 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
 
 QString QDeclarativeTextPrivate::elideChar = QString(0x2026);
@@ -90,7 +99,8 @@ QString QDeclarativeTextPrivate::elideChar = QString(0x2026);
 QDeclarativeTextPrivate::QDeclarativeTextPrivate()
 : color((QRgb)0), style(QDeclarativeText::Normal), hAlign(QDeclarativeText::AlignLeft), 
   vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone),
-  format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap), lineCount(1), truncated(false), maximumLineCount(INT_MAX),
+  format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap), lineHeight(1), lineHeightMode(QDeclarativeText::MultiplyHeight),
+  lineCount(1), truncated(false), maximumLineCount(INT_MAX),
   maximumLineCountValid(false), imageCacheDirty(true), updateOnComponentComplete(true), richText(false), singleline(false),
   cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), naturalWidth(0), doc(0)
 {
@@ -175,6 +185,15 @@ void QTextDocumentWithImageResources::setText(const QString &text)
 
 QSet<QUrl> QTextDocumentWithImageResources::errors;
 
+QDeclarativeTextDocumentLayout::QDeclarativeTextDocumentLayout(QTextDocument *doc)
+    : QTextDocumentLayout(doc) {
+}
+
+void QDeclarativeTextDocumentLayout::setLineHeight(qreal lineHeight, QDeclarativeText::LineHeightMode mode = QDeclarativeText::MultiplyHeight)
+{
+    QTextDocumentLayout::setLineHeight(lineHeight, QTextDocumentLayout::LineHeightMode(mode));
+}
+
 QDeclarativeTextPrivate::~QDeclarativeTextPrivate()
 {
 }
@@ -220,6 +239,11 @@ void QDeclarativeTextPrivate::updateLayout()
             singleline = false;
             QDeclarativeStyledText::parse(text, layout);
         }
+    } else {
+        ensureDoc();
+        QDeclarativeTextDocumentLayout *layout = new QDeclarativeTextDocumentLayout(doc);
+        layout->setLineHeight(lineHeight, lineHeightMode);
+        doc->setDocumentLayout(layout);
     }
 
     updateSize();
@@ -444,7 +468,7 @@ QSize QDeclarativeTextPrivate::setupTextLayout()
     for (int i = 0; i < layout.lineCount(); ++i) {
         QTextLine line = layout.lineAt(i);
         line.setPosition(QPointF(0, height));
-        height += line.height();
+        height += (lineHeightMode == QDeclarativeText::PixelHeight) ? lineHeight : line.height() * lineHeight;
 
         if (!cacheAllTextAsImage) {
             if ((hAlignment == QDeclarativeText::AlignLeft) || (hAlignment == QDeclarativeText::AlignJustify)) {
@@ -468,7 +492,7 @@ QSize QDeclarativeTextPrivate::setupTextLayout()
         emit q->lineCountChanged();
     }
 
-    return layout.boundingRect().toAlignedRect().size();
+    return QSize(qCeil(widthUsed), qCeil(height));
 }
 
 /*!
@@ -1404,6 +1428,60 @@ qreal QDeclarativeText::paintedHeight() const
 }
 
 /*!
+    \qmlproperty real Text::lineHeight
+
+    Sets the line height for the text.
+    The value can be in pixels or a multiplier depending on lineHeightMode.
+
+*/
+qreal QDeclarativeText::lineHeight() const
+{
+    Q_D(const QDeclarativeText);
+    return d->lineHeight;
+}
+
+void QDeclarativeText::setLineHeight(qreal lineHeight)
+{
+    Q_D(QDeclarativeText);
+
+    if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
+        return;
+
+    d->lineHeight = lineHeight;
+    d->updateLayout();
+    emit lineHeightChanged(lineHeight);
+}
+
+/*!
+    \qmlproperty real Text::lineHeightMode
+
+    This property determines how the line height is specified.
+    The possible values are:
+
+    \list
+    \o Text.MultiplyHeight (default) - specifies a line height multiplier,
+    \o Text.PixelHeight - specifies the line height in pixels.
+    \endlist
+*/
+QDeclarativeText::LineHeightMode QDeclarativeText::lineHeightMode() const
+{
+    Q_D(const QDeclarativeText);
+    return d->lineHeightMode;
+}
+
+void QDeclarativeText::setLineHeightMode(LineHeightMode mode)
+{
+    Q_D(QDeclarativeText);
+    if (mode == d->lineHeightMode)
+        return;
+
+    d->lineHeightMode = mode;
+    d->updateLayout();
+
+    emit lineHeightModeChanged(mode);
+}
+
+/*!
     Returns the number of resources (images) that are being loaded asynchronously.
 */
 int QDeclarativeText::resourcesLoading() const
diff --git a/src/declarative/graphicsitems/qdeclarativetext_p.h b/src/declarative/graphicsitems/qdeclarativetext_p.h
index 58751e7..f3697d5 100644
--- a/src/declarative/graphicsitems/qdeclarativetext_p.h
+++ b/src/declarative/graphicsitems/qdeclarativetext_p.h
@@ -62,6 +62,7 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeText : public QDeclarativeImplici
     Q_ENUMS(TextFormat)
     Q_ENUMS(TextElideMode)
     Q_ENUMS(WrapMode)
+    Q_ENUMS(LineHeightMode)
 
     Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
     Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
@@ -79,6 +80,8 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeText : public QDeclarativeImplici
     Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode?
     Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged)
     Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged)
+    Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged REVISION 1)
+    Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged REVISION 1)
 
 public:
     QDeclarativeText(QDeclarativeItem *parent=0);
@@ -111,6 +114,8 @@ public:
                     Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere
                   };
 
+    enum LineHeightMode { MultiplyHeight, PixelHeight };
+
     QString text() const;
     void setText(const QString &);
 
@@ -148,6 +153,12 @@ public:
     TextElideMode elideMode() const;
     void setElideMode(TextElideMode);
 
+    qreal lineHeight() const;
+    void setLineHeight(qreal lineHeight);
+
+    LineHeightMode lineHeightMode() const;
+    void setLineHeightMode(LineHeightMode);
+
     void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
 
     virtual void componentComplete();
@@ -175,6 +186,8 @@ Q_SIGNALS:
     void textFormatChanged(TextFormat textFormat);
     void elideModeChanged(TextElideMode mode);
     void paintedSizeChanged();
+    Q_REVISION(1) void lineHeightChanged(qreal lineHeight);
+    Q_REVISION(1) void lineHeightModeChanged(LineHeightMode mode);
 
 protected:
     void mousePressEvent(QGraphicsSceneMouseEvent *event);
diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h
index 01e0ab5..36ae123 100644
--- a/src/declarative/graphicsitems/qdeclarativetext_p_p.h
+++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h
@@ -89,6 +89,8 @@ public:
     QDeclarativeText::TextElideMode elideMode;
     QDeclarativeText::TextFormat format;
     QDeclarativeText::WrapMode wrapMode;
+    qreal lineHeight;
+    QDeclarativeText::LineHeightMode lineHeightMode;
     int lineCount;
     bool truncated;
     int maximumLineCount;
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index d721c91..c1c3768 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -513,6 +513,9 @@ public:
 
     qreal scaleToDevice(qreal value) const;
     QFixed scaleToDevice(QFixed value) const;
+
+    qreal lineH;
+    QTextDocumentLayout::LineHeightMode lineHeightMode;
 };
 
 QTextDocumentLayoutPrivate::QTextDocumentLayoutPrivate()
@@ -520,7 +523,9 @@ QTextDocumentLayoutPrivate::QTextDocumentLayoutPrivate()
       cursorWidth(1),
       currentLazyLayoutPosition(-1),
       lazyLayoutStepSize(1000),
-      lastPageCount(-1)
+      lastPageCount(-1),
+      lineH(1),
+      lineHeightMode(QTextDocumentLayout::MultiplyHeight)
 {
     showLayoutProgress = true;
     insideDocumentChange = false;
@@ -2639,7 +2644,8 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
 
             }
 
-            QFixed lineHeight = QFixed::fromReal(line.height());
+            QFixed lineHeight = (lineHeightMode == QTextDocumentLayout::PixelHeight) ? QFixed::fromReal(lineH) : QFixed::fromReal(line.height() * lineH);
+
             if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom) {
                 layoutStruct->newPage();
 
@@ -2714,6 +2720,13 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
     }
 }
 
+void QTextDocumentLayout::setLineHeight(qreal lineH, QTextDocumentLayout::LineHeightMode mode = QTextDocumentLayout::MultiplyHeight)
+{
+    Q_D(QTextDocumentLayout);
+    d->lineH = lineH;
+    d->lineHeightMode = mode;
+}
+
 void QTextDocumentLayoutPrivate::floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct,
                                               QFixed *left, QFixed *right) const
 {
diff --git a/src/gui/text/qtextdocumentlayout_p.h b/src/gui/text/qtextdocumentlayout_p.h
index 3c0383c..efc408b 100644
--- a/src/gui/text/qtextdocumentlayout_p.h
+++ b/src/gui/text/qtextdocumentlayout_p.h
@@ -63,7 +63,7 @@ class QTextListFormat;
 
 class QTextDocumentLayoutPrivate;
 
-class Q_AUTOTEST_EXPORT QTextDocumentLayout : public QAbstractTextDocumentLayout
+class Q_GUI_EXPORT QTextDocumentLayout : public QAbstractTextDocumentLayout
 {
     Q_DECLARE_PRIVATE(QTextDocumentLayout)
     Q_OBJECT
@@ -109,6 +109,9 @@ protected:
     void drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
                           int posInDocument, const QTextFormat &format);
     virtual void timerEvent(QTimerEvent *e);
+    enum LineHeightMode { MultiplyHeight, PixelHeight };
+    void setLineHeight(qreal lineHeight, QTextDocumentLayout::LineHeightMode mode);
+
 private:
     QRectF doLayout(int from, int oldLength, int length);
     void layoutFinished();
diff --git a/tests/auto/declarative/qdeclarativetext/data/lineHeight.qml b/tests/auto/declarative/qdeclarativetext/data/lineHeight.qml
new file mode 100644
index 0000000..851d871
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativetext/data/lineHeight.qml
@@ -0,0 +1,15 @@
+import QtQuick 1.1
+
+Item {
+    width: 200
+    height: 200
+
+    Text {
+        id: myText
+        objectName: "myText"
+        width: 200
+        wrapMode: Text.WordWrap
+        font.pixelSize: 13
+        text: "Lorem ipsum sit amet, consectetur adipiscing elit. Integer felis nisl, varius in pretium nec, venenatis non erat. Proin lobortis interdum dictum."
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp
index c9b5295..b96fbff 100644
--- a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp
+++ b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp
@@ -81,6 +81,7 @@ private slots:
     void embeddedImages();
 
     void lineCount();
+    void lineHeight();
 
     // ### these tests may be trivial    
     void horizontalAlignment();
@@ -1082,6 +1083,37 @@ void tst_qdeclarativetext::lineCount()
     QCOMPARE(myText->maximumLineCount(), 2);
 }
 
+void tst_qdeclarativetext::lineHeight()
+{
+    QDeclarativeView *canvas = createView(SRCDIR "/data/lineHeight.qml");
+
+    QDeclarativeText *myText = canvas->rootObject()->findChild<QDeclarativeText*>("myText");
+    QVERIFY(myText != 0);
+
+    QVERIFY(myText->lineHeight() == 1);
+    QVERIFY(myText->lineHeightMode() == QDeclarativeText::MultiplyHeight);
+
+    qreal h = myText->height();
+    myText->setLineHeight(1.5);
+    QVERIFY(myText->height() == h * 1.5);
+
+    myText->setLineHeightMode(QDeclarativeText::PixelHeight);
+    myText->setLineHeight(20);
+    QCOMPARE(myText->height(), 120.0);
+
+    myText->setText("Lorem ipsum sit <b>amet</b>, consectetur adipiscing elit. Integer felis nisl, varius in pretium nec, venenatis non erat. Proin lobortis interdum dictum.");
+    myText->setLineHeightMode(QDeclarativeText::MultiplyHeight);
+    myText->setLineHeight(1);
+
+    qreal h2 = myText->height();
+    myText->setLineHeight(1.25);
+    QVERIFY(myText->height() == h2 * 1.25);
+
+    myText->setLineHeightMode(QDeclarativeText::PixelHeight);
+    myText->setLineHeight(10);
+    QCOMPARE(myText->height(), 60.0);
+}
+
 void tst_qdeclarativetext::implicitSize_data()
 {
     QTest::addColumn<QString>("text");
-- 
cgit v0.12