[Qt-interest] My first Qt patch

Joshua Grauman jnfo-c at grauman.com
Wed Feb 10 01:13:25 CET 2010


Hello all,

I had requested a new feature (line spacing options) that I'd really like 
to see in Qt a bit ago, and then I thought, "Hey, this is open source, why 
don't I just add it myself." So while I've helped find bugs, etc., in the 
past this is my first attempt at adding a new feature to Qt and would like 
some input. First I'd like to know what is the preferred way of sending in 
a patch for Qt? I've included everything here but if there is a better 
place to send it, let me know. Otherwise, besides how to try to get the 
patch included, other comments are appreciated as well.

I googled and found a recent doc about sending a patch in for Qt Creator, 
I'm assuming the same basic rules apply. I read about style, etc., and 
think I've done everything the Qt way, as far as I can tell. I'd prefer to 
send in a diff, as I'd rather not have to learn git at this point.

It turned out to be a bit bigger and more complex than I was originally 
intending. The patch adds line spacing options to Qt, as in all modern 
word processors. I primarily checked OpenOffice as a reference for how to 
position the lines based on the various types of line spacing as well as 
where to do the page breaks.

I tried to be careful not to add extra cycles to the default code path 
(single spacing) which is the default.

The interface is via QTextBlockFormat. The following options are 
available:

QTextBlockFormat::SingleHeight
QTextBlockFormat::ProportionalHeight
QTextBlockFormat::FixedHeight
QTextBlockFormat::AtLeastHeight
QTextBlockFormat::LineDistanceHeight

They are set via:
    void QTextBlockFormat::setLineHeight(qreal height, int heightType)

All the height values are in pixels (except proportional, which is a 
percentage). The fixed pixel distances are scaled for use during printing.

and can be read back via:
    qreal QTextBlockFormat::lineHeight()
    int QTextBlockFormat::lineHeightType()

There is also a function which returns back what the line height will be 
based upon the given script line height and scaling (during printing):
    qreal QTextBlockFormat::lineHeight(qreal scriptLineHeight, qreal scaling)

I also added support for the standard css property 'line-height' with the 
following variants supported:

line-height:150%
line-height:40px
line-height:normal

I also added support for the following two non-standard css properties to 
support the AtLeastHeight and LineDistanceHeight options which aren't 
supported in standard css. I thought there is probably a better way to 
support these though... so I'm open to better syntax here. I've included 
them as an easy way to test these features.

line-height:40al
line-height:40ld

The change touches 5 files in src/gui/text.

Here is the diff. I haven't added documentation yet, but can help do that 
too. I'm happy to sign off copyright on the patch. It's a diff off of 
4.6.1. Input is appreciated. Hopefully this is pretty close as I ended up 
putting more time than I was originally thinking of doing as I'd love to 
see this get into Qt ;) Thanks. Let me know if explanation/comments for 
anything is needed. I've tested it quite a bit and everything looks good 
so far, but I could definitely use another set of eyes or two looking over 
the patch...

Josh
-------------- next part --------------
--- qcssparser.cpp.org	2010-02-05 22:25:52.380701205 -0800
+++ qcssparser.cpp	2010-02-06 13:19:38.121957202 -0800
@@ -120,6 +120,7 @@ static const QCssKnownValue properties[N
     { "image", QtImage },
     { "image-position", QtImageAlignment },
     { "left", Left },
+    { "line-height", LineHeight },
     { "list-style", ListStyle },
     { "list-style-type", ListStyleType },
     { "margin" , Margin },
--- qcssparser_p.h.org	2010-02-05 22:25:56.500709921 -0800
+++ qcssparser_p.h	2010-02-06 13:19:38.121957202 -0800
@@ -178,6 +178,7 @@ enum Property {
     OutlineBottomRightRadius,
     FontVariant,
     TextTransform,
+    LineHeight,
     NumProperties
 };
 
--- qtextdocumentlayout.cpp.org	2010-02-05 22:25:52.401950025 -0800
+++ qtextdocumentlayout.cpp	2010-02-09 16:11:03.700449047 -0800
@@ -2508,6 +2508,23 @@ void QTextDocumentLayoutPrivate::layoutF
     fd->currentLayoutStruct = 0;
 }
 
+static inline void getLineHeightParams(const QTextBlockFormat &blockFormat, const QTextLine &line, qreal scaling,
+                                       QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight)
+{           
+    *lineHeight = QFixed::fromReal(blockFormat.lineHeight(line.height(), scaling));
+    if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight || blockFormat.lineHeightType() == QTextBlockFormat::AtLeastHeight) {
+        *lineBreakHeight = *lineHeight;
+        if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight)
+            *lineAdjustment = QFixed::fromReal(line.ascent() + qMax(line.leading(), 0.0)) - ((*lineHeight * 4) / 5); 
+        else
+            *lineAdjustment = QFixed::fromReal(line.height()) - *lineHeight;
+    }
+    else {
+        *lineBreakHeight = QFixed::fromReal(line.height());
+        *lineAdjustment = 0;
+    }
+}
+
 void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat,
                                              QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat)
 {
@@ -2643,8 +2660,11 @@ void QTextDocumentLayoutPrivate::layoutB
 
             }
 
-            QFixed lineHeight = QFixed::fromReal(line.height());
-            if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom) {
+            QFixed lineBreakHeight, lineHeight, lineAdjustment;
+            qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ? qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
+            getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight);
+
+            if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom) {
                 layoutStruct->newPage();
 
                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
@@ -2656,8 +2676,9 @@ void QTextDocumentLayoutPrivate::layoutB
                     right -= text_indent;
             }
 
-            line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy).toReal()));
+            line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy - lineAdjustment).toReal()));
             layoutStruct->y += lineHeight;
+
             layoutStruct->contentsWidth
                 = qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin);
 
@@ -2676,11 +2697,15 @@ void QTextDocumentLayoutPrivate::layoutB
             QTextLine line = tl->lineAt(i);
             layoutStruct->contentsWidth
                 = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin);
-            const QFixed lineHeight = QFixed::fromReal(line.height());
+
+            QFixed lineBreakHeight, lineHeight, lineAdjustment;
+            qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ? qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
+            getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight);
+
             if (layoutStruct->pageHeight != QFIXED_MAX) {
-                if (layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom)
+                if (layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom)
                     layoutStruct->newPage();
-                line.setPosition(QPointF(line.position().x(), layoutStruct->y.toReal() - tl->position().y()));
+                line.setPosition(QPointF(line.position().x(), (layoutStruct->y - lineAdjustment).toReal() - tl->position().y()));
             }
             layoutStruct->y += lineHeight;
         }
--- qtextformat.h.org	2010-02-05 22:25:56.510704501 -0800
+++ qtextformat.h	2010-02-09 16:07:46.550449039 -0800
@@ -164,6 +164,8 @@ public:
         TextIndent = 0x1034,
         TabPositions = 0x1035,
         BlockIndent = 0x1040,
+        LineHeight = 0x1048,
+        LineHeightType = 0x1049,
         BlockNonBreakableLines = 0x1050,
         BlockTrailingHorizontalRulerWidth = 0x1060,
 
@@ -529,6 +531,14 @@ inline void QTextCharFormat::setTableCel
 class Q_GUI_EXPORT QTextBlockFormat : public QTextFormat
 {
 public:
+    enum LineHeightTypes {
+        SingleHeight = 0,
+        ProportionalHeight = 1,
+        FixedHeight = 2,
+        AtLeastHeight = 3,
+        LineDistanceHeight = 4
+    };
+
     QTextBlockFormat();
 
     bool isValid() const { return isBlockFormat(); }
@@ -566,6 +576,14 @@ public:
     inline int indent() const
     { return intProperty(BlockIndent); }
 
+    inline void setLineHeight(qreal height, int heightType)
+    { setProperty(LineHeight, height); setProperty(LineHeightType, heightType); }
+    inline qreal lineHeight(qreal scriptLineHeight, qreal scaling) const;
+    inline qreal lineHeight() const
+    { return doubleProperty(LineHeight); }                    
+    inline int lineHeightType() const
+    { return intProperty(LineHeightType); }
+
     inline void setNonBreakableLines(bool b)
     { setProperty(BlockNonBreakableLines, b); }
     inline bool nonBreakableLines() const
@@ -590,6 +608,23 @@ inline void QTextBlockFormat::setAlignme
 inline void QTextBlockFormat::setIndent(int aindent)
 { setProperty(BlockIndent, aindent); }
 
+inline qreal QTextBlockFormat::lineHeight(qreal scriptLineHeight, qreal scaling = 1.0) const
+{
+  switch(intProperty(LineHeightType)) {
+    case SingleHeight:
+      return(scriptLineHeight);
+    case ProportionalHeight:
+      return(scriptLineHeight * doubleProperty(LineHeight) / 100.0);
+    case FixedHeight:
+      return(doubleProperty(LineHeight) * scaling);
+    case AtLeastHeight:
+      return(qMax(scriptLineHeight, doubleProperty(LineHeight) * scaling));
+    case LineDistanceHeight:
+      return(scriptLineHeight + doubleProperty(LineHeight) * scaling);
+  }
+  return(0);
+}
+
 class Q_GUI_EXPORT QTextListFormat : public QTextFormat
 {
 public:
--- qtexthtmlparser.cpp.org	2010-02-05 22:25:52.401950025 -0800
+++ qtexthtmlparser.cpp	2010-02-07 17:32:51.090449028 -0800
@@ -1256,6 +1256,24 @@ void QTextHtmlParserNode::applyCssDeclar
             if (decl.realValue(&indent, "px"))
                 blockFormat.setTextIndent(indent);
             break; }
+        case QCss::LineHeight: {
+            qreal lineHeight;
+            if (decl.realValue(&lineHeight, "px"))
+                blockFormat.setLineHeight(lineHeight,QTextBlockFormat::FixedHeight);
+            else if (decl.realValue(&lineHeight, "al"))
+                blockFormat.setLineHeight(lineHeight,QTextBlockFormat::AtLeastHeight);
+            else if (decl.realValue(&lineHeight, "ld"))
+                blockFormat.setLineHeight(lineHeight,QTextBlockFormat::LineDistanceHeight);
+            else {
+                bool ok;
+                QString value = decl.d->values.first().toString();
+                lineHeight = value.toDouble(&ok);
+                if (ok)
+                    blockFormat.setLineHeight(lineHeight,QTextBlockFormat::ProportionalHeight);
+                else
+                    blockFormat.setLineHeight(0,QTextBlockFormat::SingleHeight);
+            }
+            break; }
         case QCss::QtListIndent:
             if (decl.intValue(&cssListIndent))
                 hasCssListIndent = true;


More information about the Qt-interest-old mailing list