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 } |
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) { |