src/gui/text/qtextlayout.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
child 37 758a864f9613
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
    67 static inline QFixed leadingSpaceWidth(QTextEngine *eng, const QScriptLine &line)
    67 static inline QFixed leadingSpaceWidth(QTextEngine *eng, const QScriptLine &line)
    68 {
    68 {
    69     if (!line.hasTrailingSpaces
    69     if (!line.hasTrailingSpaces
    70         || (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
    70         || (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
    71         || !(eng->option.alignment() & Qt::AlignRight)
    71         || !(eng->option.alignment() & Qt::AlignRight)
    72         || (eng->option.textDirection() != Qt::RightToLeft))
    72         || !eng->isRightToLeft())
    73         return QFixed();
    73         return QFixed();
    74 
    74 
    75     int pos = line.length;
    75     int pos = line.length;
    76     const HB_CharAttributes *attributes = eng->attributes();
    76     const HB_CharAttributes *attributes = eng->attributes();
    77     while (pos > 0 && attributes[line.from + pos - 1].whiteSpace)
    77     while (pos > 0 && attributes[line.from + pos - 1].whiteSpace)
    84     QFixed x = 0;
    84     QFixed x = 0;
    85     eng->justify(line);
    85     eng->justify(line);
    86     // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
    86     // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
    87     if (!line.justified && line.width != QFIXED_MAX) {
    87     if (!line.justified && line.width != QFIXED_MAX) {
    88         int align = eng->option.alignment();
    88         int align = eng->option.alignment();
    89         if (align & Qt::AlignJustify && eng->option.textDirection() == Qt::RightToLeft)
    89         if (align & Qt::AlignJustify && eng->isRightToLeft())
    90             align = Qt::AlignRight;
    90             align = Qt::AlignRight;
    91         if (align & Qt::AlignRight)
    91         if (align & Qt::AlignRight)
    92             x = line.width - (line.textWidth + leadingSpaceWidth(eng, line));
    92             x = line.width - (line.textAdvance + leadingSpaceWidth(eng, line));
    93         else if (align & Qt::AlignHCenter)
    93         else if (align & Qt::AlignHCenter)
    94             x = (line.width - line.textWidth)/2;
    94             x = (line.width - line.textAdvance)/2;
    95     }
    95     }
    96     return x;
    96     return x;
    97 }
    97 }
    98 
    98 
    99 /*!
    99 /*!
   280 
   280 
   281 /*!
   281 /*!
   282     \class QTextLayout
   282     \class QTextLayout
   283     \reentrant
   283     \reentrant
   284 
   284 
   285     \brief The QTextLayout class is used to lay out and paint a single
   285     \brief The QTextLayout class is used to lay out and render text.
   286     paragraph of text.
       
   287 
   286 
   288     \ingroup richtext-processing
   287     \ingroup richtext-processing
   289 
   288 
   290     It offers most features expected from a modern text layout
   289     It offers many features expected from a modern text layout
   291     engine, including Unicode compliant rendering, line breaking and
   290     engine, including Unicode compliant rendering, line breaking and
   292     handling of cursor positioning. It can also produce and render
   291     handling of cursor positioning. It can also produce and render
   293     device independent layout, something that is important for WYSIWYG
   292     device independent layout, something that is important for WYSIWYG
   294     applications.
   293     applications.
   295 
   294 
   296     The class has a rather low level API and unless you intend to
   295     The class has a rather low level API and unless you intend to
   297     implement your own text rendering for some specialized widget, you
   296     implement your own text rendering for some specialized widget, you
   298     probably won't need to use it directly.
   297     probably won't need to use it directly.
   299 
   298 
   300     QTextLayout can currently deal with plain text and rich text
   299     QTextLayout can be used with both plain and rich text.
   301     paragraphs that are part of a QTextDocument.
   300 
   302 
   301     QTextLayout can be used to create a sequence of QTextLine
   303     QTextLayout can be used to create a sequence of QTextLine's with
   302     instances with given widths and can position them independently
   304     given widths and can position them independently on the screen.
   303     on the screen. Once the layout is done, these lines can be drawn
   305     Once the layout is done, these lines can be drawn on a paint
   304     on a paint device.
   306     device.
   305 
   307 
   306     The text to be laid out can be provided in the constructor or set with
   308     Here's some code snippet that presents the layout phase:
   307     setText().
       
   308 
       
   309     The layout can be seen as a sequence of QTextLine objects; use createLine()
       
   310     to create a QTextLine instance, and lineAt() or lineForTextPosition() to retrieve
       
   311     created lines.
       
   312 
       
   313     Here is a code snippet that demonstrates the layout phase:
   309     \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
   314     \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
   310 
   315 
   311     The text can be drawn by calling the layout's draw() function:
   316     The text can then be rendered by calling the layout's draw() function:
   312     \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 1
   317     \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 1
   313 
   318 
   314     The text layout's text is set in the constructor or with
   319     For a given position in the text you can find a valid cursor position with
   315     setText(). The layout can be seen as a sequence of QTextLine
   320     isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
   316     objects; use lineAt() or lineForTextPosition() to get a QTextLine,
   321 
   317     createLine() to create one. For a given position in the text you
   322     The QTextLayout itself can be positioned with setPosition(); it has a
   318     can find a valid cursor position with isValidCursorPosition(),
   323     boundingRect(), and a minimumWidth() and a maximumWidth().
   319     nextCursorPosition(), and previousCursorPosition(). The layout
   324 
   320     itself can be positioned with setPosition(); it has a
   325     \sa QStaticText
   321     boundingRect(), and a minimumWidth() and a maximumWidth(). A text
       
   322     layout can be drawn on a painter device using draw().
       
   323 
   326 
   324 */
   327 */
   325 
   328 
   326 /*!
   329 /*!
   327     \enum QTextLayout::CursorMode
   330     \enum QTextLayout::CursorMode
   856 
   859 
   857     for (int i = 0; i < d->lines.size(); ++i) {
   860     for (int i = 0; i < d->lines.size(); ++i) {
   858         const QScriptLine &si = d->lines[i];
   861         const QScriptLine &si = d->lines[i];
   859         xmin = qMin(xmin, si.x);
   862         xmin = qMin(xmin, si.x);
   860         ymin = qMin(ymin, si.y);
   863         ymin = qMin(ymin, si.y);
   861         xmax = qMax(xmax, si.x+qMax(si.width, si.textWidth));
   864         QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
       
   865         xmax = qMax(xmax, si.x+lineWidth);
   862         // ### shouldn't the ascent be used in ymin???
   866         // ### shouldn't the ascent be used in ymin???
   863         ymax = qMax(ymax, si.y+si.height());
   867         ymax = qMax(ymax, si.y+si.height());
   864     }
   868     }
   865     return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
   869     return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
   866 }
   870 }
  1334     qreal x = position.x() + l.cursorToX(cursorPosition);
  1338     qreal x = position.x() + l.cursorToX(cursorPosition);
  1335 
  1339 
  1336     int itm = d->findItem(cursorPosition - 1);
  1340     int itm = d->findItem(cursorPosition - 1);
  1337     QFixed base = sl.base();
  1341     QFixed base = sl.base();
  1338     QFixed descent = sl.descent;
  1342     QFixed descent = sl.descent;
  1339     bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft);
  1343     bool rightToLeft = d->isRightToLeft();
  1340     if (itm >= 0) {
  1344     if (itm >= 0) {
  1341         const QScriptItem &si = d->layoutData->items.at(itm);
  1345         const QScriptItem &si = d->layoutData->items.at(itm);
  1342         if (si.ascent > 0)
  1346         if (si.ascent > 0)
  1343             base = si.ascent;
  1347             base = si.ascent;
  1344         if (si.descent > 0)
  1348         if (si.descent > 0)
  1658 
  1662 
  1659     struct LineBreakHelper
  1663     struct LineBreakHelper
  1660     {
  1664     {
  1661         LineBreakHelper()
  1665         LineBreakHelper()
  1662             : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
  1666             : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
  1663               manualWrap(false)
  1667               manualWrap(false), whiteSpaceOrObject(true)
  1664         {
  1668         {
  1665         }
  1669         }
  1666 
  1670 
  1667 
  1671 
  1668         QScriptLine tmpData;
  1672         QScriptLine tmpData;
  1681 
  1685 
  1682         QFontEngine *fontEngine;
  1686         QFontEngine *fontEngine;
  1683         const unsigned short *logClusters;
  1687         const unsigned short *logClusters;
  1684 
  1688 
  1685         bool manualWrap;
  1689         bool manualWrap;
       
  1690         bool whiteSpaceOrObject;
  1686 
  1691 
  1687         bool checkFullOtherwiseExtend(QScriptLine &line);
  1692         bool checkFullOtherwiseExtend(QScriptLine &line);
  1688 
  1693 
  1689         QFixed calculateNewWidth(const QScriptLine &line) const {
  1694         QFixed calculateNewWidth(const QScriptLine &line) const {
  1690             return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
  1695             return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
  1691                     - qMin(rightBearing, QFixed());
  1696                     - qMin(rightBearing, QFixed());
  1692         }
  1697         }
  1693 
  1698 
  1694         inline glyph_t currentGlyph() const
  1699         inline glyph_t currentGlyph() const
  1695         {
  1700         {            
  1696             Q_ASSERT(currentPosition > 0);
  1701             Q_ASSERT(currentPosition > 0);
       
  1702             Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
       
  1703 
  1697             return glyphs.glyphs[logClusters[currentPosition - 1]];
  1704             return glyphs.glyphs[logClusters[currentPosition - 1]];
  1698         }
  1705         }
  1699 
  1706 
  1700         inline void adjustRightBearing()
  1707         inline void adjustRightBearing()
  1701         {
  1708         {
  1826                                                                             current.ascent);
  1833                                                                             current.ascent);
  1827         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
  1834         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
  1828         lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
  1835         lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
  1829 
  1836 
  1830         if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
  1837         if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
       
  1838             lbh.whiteSpaceOrObject = true;
  1831             if (lbh.checkFullOtherwiseExtend(line))
  1839             if (lbh.checkFullOtherwiseExtend(line))
  1832                 goto found;
  1840                 goto found;
  1833 
  1841 
  1834             QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
  1842             QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
  1835             QFixed tabWidth = eng->calculateTabWidth(item, x);
  1843             QFixed tabWidth = eng->calculateTabWidth(item, x);
  1842             lbh.glyphCount += qRound(tabWidth / averageCharWidth);
  1850             lbh.glyphCount += qRound(tabWidth / averageCharWidth);
  1843 
  1851 
  1844             if (lbh.checkFullOtherwiseExtend(line))
  1852             if (lbh.checkFullOtherwiseExtend(line))
  1845                 goto found;
  1853                 goto found;
  1846         } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
  1854         } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
       
  1855             lbh.whiteSpaceOrObject = true;
  1847             // if the line consists only of the line separator make sure
  1856             // if the line consists only of the line separator make sure
  1848             // we have a sane height
  1857             // we have a sane height
  1849             if (!line.length && !lbh.tmpData.length)
  1858             if (!line.length && !lbh.tmpData.length)
  1850                 line.setDefaultHeight(eng);
  1859                 line.setDefaultHeight(eng);
  1851             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
  1860             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
  1855                 lbh.tmpData.length++;
  1864                 lbh.tmpData.length++;
  1856             }
  1865             }
  1857             line += lbh.tmpData;
  1866             line += lbh.tmpData;
  1858             goto found;
  1867             goto found;
  1859         } else if (current.analysis.flags == QScriptAnalysis::Object) {
  1868         } else if (current.analysis.flags == QScriptAnalysis::Object) {
       
  1869             lbh.whiteSpaceOrObject = true;
  1860             lbh.tmpData.length++;
  1870             lbh.tmpData.length++;
  1861 
  1871 
  1862             QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
  1872             QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
  1863             if (eng->block.docHandle())
  1873             if (eng->block.docHandle())
  1864                 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
  1874                 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
  1868             newItem = item + 1;
  1878             newItem = item + 1;
  1869             ++lbh.glyphCount;
  1879             ++lbh.glyphCount;
  1870             if (lbh.checkFullOtherwiseExtend(line))
  1880             if (lbh.checkFullOtherwiseExtend(line))
  1871                 goto found;
  1881                 goto found;
  1872         } else if (attributes[lbh.currentPosition].whiteSpace) {
  1882         } else if (attributes[lbh.currentPosition].whiteSpace) {
       
  1883             lbh.whiteSpaceOrObject = true;
  1873             while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
  1884             while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
  1874                 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
  1885                 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
  1875                                current, lbh.logClusters, lbh.glyphs);
  1886                                current, lbh.logClusters, lbh.glyphs);
  1876 
  1887 
  1877             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  1888             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  1878                 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
  1889                 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
  1879                 goto found;
  1890                 goto found;
  1880             }
  1891             }
  1881         } else {
  1892         } else {
       
  1893             lbh.whiteSpaceOrObject = false;
  1882             bool sb_or_ws = false;
  1894             bool sb_or_ws = false;
  1883             do {
  1895             do {
  1884                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
  1896                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
  1885                                current, lbh.logClusters, lbh.glyphs);
  1897                                current, lbh.logClusters, lbh.glyphs);
  1886 
  1898 
  1921             // of the advance of the glyph, the bearing will be negative. We flip the sign
  1933             // of the advance of the glyph, the bearing will be negative. We flip the sign
  1922             // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
  1934             // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
  1923             // We ignore the right bearing if the minimum negative bearing is too little to
  1935             // We ignore the right bearing if the minimum negative bearing is too little to
  1924             // expand the text beyond the edge.
  1936             // expand the text beyond the edge.
  1925             if (sb_or_ws|breakany) {
  1937             if (sb_or_ws|breakany) {
  1926                 if (lbh.calculateNewWidth(line) + lbh.minimumRightBearing > line.width)
  1938                 if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
  1927                     lbh.adjustRightBearing();
  1939                     lbh.adjustRightBearing();
  1928                 if (lbh.checkFullOtherwiseExtend(line)) {
  1940                 if (lbh.checkFullOtherwiseExtend(line)) {
  1929                     if (!breakany) {
  1941                     if (!breakany) {
  1930                         line.textWidth += lbh.softHyphenWidth;
  1942                         line.textWidth += lbh.softHyphenWidth;
  1931                     }
  1943                     }
  1938             newItem = item + 1;
  1950             newItem = item + 1;
  1939     }
  1951     }
  1940     LB_DEBUG("reached end of line");
  1952     LB_DEBUG("reached end of line");
  1941     lbh.checkFullOtherwiseExtend(line);
  1953     lbh.checkFullOtherwiseExtend(line);
  1942 found:       
  1954 found:       
  1943     if (lbh.rightBearing > 0) // If right bearing has not yet been adjusted
  1955     if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
  1944         lbh.adjustRightBearing();
  1956         lbh.adjustRightBearing();
  1945     line.textAdvance = line.textWidth;
  1957     line.textAdvance = line.textWidth;
  1946     line.textWidth -= qMin(QFixed(), lbh.rightBearing);
  1958     line.textWidth -= qMin(QFixed(), lbh.rightBearing);
  1947 
  1959 
  1948     if (line.length == 0) {
  1960     if (line.length == 0) {