From 04d18b38c38c5ff623b30366ea08d56128b9b7d0 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Thu, 22 Oct 2009 12:28:27 +0200 Subject: Line spacing fixes QTextEdit (via QTextLayout) and QPlainTextEdit in Qt used to ignore any font leading but added one extra pixel in QFontMetrics. With many freetype fonts, this resulted in a "spacy" text layout. The necessary fixes on X11 and Windows were to take (positive) leading into account, to make the font database convert point sizes to pixel sizes without rounding to plain integer values, and to subtract the extra pixel from QFontMetrics from the font engines' descent value. The change also fixes several places in styles and widgets, where QFontMetrics::lineSpacing() was wrongly used instead of QFontMetrics::height(). Ideally we should also handle negative leading, which would require additional and bigger code changes in QTextLayout and QPlainTextEdit. In addition, all other editors we have tested seem to ignore leading on X11. If we choose to believe the values provided by freetype, our text layout would be one pixel smaller than everybody else's. On the Mac, this change does nothing. There our layout is still too spacy, and for smaller fonts quite ugly compared to native Mac applications. Done with mae. Reviewed-by: mae --- src/gui/embedded/qdecorationdefault_qws.cpp | 2 +- src/gui/embedded/qdecorationwindows_qws.cpp | 2 +- src/gui/itemviews/qheaderview.cpp | 2 +- src/gui/styles/qcleanlooksstyle.cpp | 2 +- src/gui/styles/qcommonstyle.cpp | 6 +-- src/gui/styles/qmotifstyle.cpp | 4 +- src/gui/styles/qplastiquestyle.cpp | 8 +-- src/gui/styles/qstylesheetstyle.cpp | 2 +- src/gui/text/qfont.cpp | 2 +- src/gui/text/qfont_p.h | 2 +- src/gui/text/qfontdatabase.cpp | 2 +- src/gui/text/qfontdatabase_win.cpp | 12 +++-- src/gui/text/qfontdatabase_x11.cpp | 14 ++++-- src/gui/text/qfontengine_ft.cpp | 10 ++-- src/gui/text/qfontengine_win.cpp | 4 +- src/gui/text/qtextdocumentlayout.cpp | 5 +- src/gui/text/qtextengine.cpp | 22 ++++++--- src/gui/text/qtextengine_mac.cpp | 2 +- src/gui/text/qtextengine_p.h | 17 +++++-- src/gui/text/qtextlayout.cpp | 76 ++++++++++++++++++++++++----- src/gui/text/qtextlayout.h | 4 ++ src/gui/text/text.pri | 1 + src/gui/widgets/qcombobox.cpp | 2 +- src/gui/widgets/qdockwidget.cpp | 2 +- src/gui/widgets/qfontcombobox.cpp | 2 +- src/gui/widgets/qlineedit.cpp | 2 +- src/gui/widgets/qplaintextedit.cpp | 5 +- 27 files changed, 149 insertions(+), 65 deletions(-) diff --git a/src/gui/embedded/qdecorationdefault_qws.cpp b/src/gui/embedded/qdecorationdefault_qws.cpp index 5bc8826..70389d3 100644 --- a/src/gui/embedded/qdecorationdefault_qws.cpp +++ b/src/gui/embedded/qdecorationdefault_qws.cpp @@ -394,7 +394,7 @@ QPixmap QDecorationDefault::pixmapFor(const QWidget *widget, */ int QDecorationDefault::titleBarHeight(const QWidget *) { - return qMax(20, QApplication::fontMetrics().lineSpacing() + BORDER_WIDTH); + return qMax(20, QApplication::fontMetrics().height() + BORDER_WIDTH); } /*! diff --git a/src/gui/embedded/qdecorationwindows_qws.cpp b/src/gui/embedded/qdecorationwindows_qws.cpp index 77393cf..7764ca5 100644 --- a/src/gui/embedded/qdecorationwindows_qws.cpp +++ b/src/gui/embedded/qdecorationwindows_qws.cpp @@ -216,7 +216,7 @@ QRegion QDecorationWindows::region(const QWidget *widget, const QRect &rect, int bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; const QFontMetrics fontMetrics = QApplication::fontMetrics(); - int titleHeight = hasTitle ? qMax(20, fontMetrics.lineSpacing()) : 0; + int titleHeight = hasTitle ? qMax(20, fontMetrics.height()) : 0; int state = widget->windowState(); bool isMinimized = state & Qt::WindowMinimized; bool isMaximized = state & Qt::WindowMaximized; diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp index fc9820f..3bd9a19 100644 --- a/src/gui/itemviews/qheaderview.cpp +++ b/src/gui/itemviews/qheaderview.cpp @@ -1419,7 +1419,7 @@ int QHeaderView::minimumSectionSize() const int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this); if (d->orientation == Qt::Horizontal) return qMax(strut.width(), (fontMetrics().maxWidth() + margin)); - return qMax(strut.height(), (fontMetrics().lineSpacing() + margin)); + return qMax(strut.height(), (fontMetrics().height() + margin)); } return d->minimumSectionSize; } diff --git a/src/gui/styles/qcleanlooksstyle.cpp b/src/gui/styles/qcleanlooksstyle.cpp index fabd7ca..fc12cfe 100644 --- a/src/gui/styles/qcleanlooksstyle.cpp +++ b/src/gui/styles/qcleanlooksstyle.cpp @@ -3866,7 +3866,7 @@ QSize QCleanlooksStyle::sizeFromContents(ContentsType type, const QStyleOption * if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast(option)) { if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { if (!menuItem->text.isEmpty()) { - newSize.setHeight(menuItem->fontMetrics.lineSpacing()); + newSize.setHeight(menuItem->fontMetrics.height()); } } #ifndef QT_NO_COMBOBOX diff --git a/src/gui/styles/qcommonstyle.cpp b/src/gui/styles/qcommonstyle.cpp index 5886512..70d130a 100644 --- a/src/gui/styles/qcommonstyle.cpp +++ b/src/gui/styles/qcommonstyle.cpp @@ -4396,13 +4396,13 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid case PM_TitleBarHeight: { if (const QStyleOptionTitleBar *tb = qstyleoption_cast(opt)) { if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) { - ret = qMax(widget ? widget->fontMetrics().lineSpacing() : opt->fontMetrics.lineSpacing(), 16); + ret = qMax(widget ? widget->fontMetrics().height() : opt->fontMetrics.height(), 16); #ifndef QT_NO_DOCKWIDGET } else if (qobject_cast(widget)) { - ret = qMax(widget->fontMetrics().lineSpacing(), int(QStyleHelper::dpiScaled(13))); + ret = qMax(widget->fontMetrics().height(), int(QStyleHelper::dpiScaled(13))); #endif } else { - ret = qMax(widget ? widget->fontMetrics().lineSpacing() : opt->fontMetrics.lineSpacing(), 18); + ret = qMax(widget ? widget->fontMetrics().height() : opt->fontMetrics.height(), 18); } } else { ret = int(QStyleHelper::dpiScaled(18.)); diff --git a/src/gui/styles/qmotifstyle.cpp b/src/gui/styles/qmotifstyle.cpp index e6c60cf..b65d45c 100644 --- a/src/gui/styles/qmotifstyle.cpp +++ b/src/gui/styles/qmotifstyle.cpp @@ -1154,7 +1154,7 @@ void QMotifStyle::drawControl(ControlElement element, const QStyleOption *opt, Q menuitem->palette, menuitem->state & State_Enabled, menuitem->text, QPalette::Text); textWidth = menuitem->fontMetrics.width(menuitem->text) + 10; - y += menuitem->fontMetrics.lineSpacing() / 2; + y += menuitem->fontMetrics.height() / 2; p->setFont(oldFont); } p->setPen(opt->palette.dark().color()); @@ -2056,7 +2056,7 @@ QMotifStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (mi->menuItemType == QStyleOptionMenuItem::Separator) { w = 10; - h = (mi->text.isEmpty()) ? motifSepHeight : mi->fontMetrics.lineSpacing(); + h = (mi->text.isEmpty()) ? motifSepHeight : mi->fontMetrics.height(); } // a little bit of border can never harm diff --git a/src/gui/styles/qplastiquestyle.cpp b/src/gui/styles/qplastiquestyle.cpp index ce2109a..09f5d36 100644 --- a/src/gui/styles/qplastiquestyle.cpp +++ b/src/gui/styles/qplastiquestyle.cpp @@ -5027,7 +5027,7 @@ QSize QPlastiqueStyle::sizeFromContents(ContentsType type, const QStyleOption *o case CT_MenuItem: if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast(option)) { if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) - newSize.setHeight(menuItem->text.isEmpty() ? 2 : menuItem->fontMetrics.lineSpacing()); + newSize.setHeight(menuItem->text.isEmpty() ? 2 : menuItem->fontMetrics.height()); } break; case CT_MenuBarItem: @@ -5607,11 +5607,11 @@ int QPlastiqueStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, #ifdef QT3_SUPPORT if (widget && widget->inherits("Q3DockWindowTitleBar")) { // Q3DockWindow has smaller title bars than QDockWidget - ret = qMax(widget->fontMetrics().lineSpacing(), 20); + ret = qMax(widget->fontMetrics().height(), 20); } else #endif - ret = qMax(widget ? widget->fontMetrics().lineSpacing() : - (option ? option->fontMetrics.lineSpacing() : 0), 30); + ret = qMax(widget ? widget->fontMetrics().height() : + (option ? option->fontMetrics.height() : 0), 30); break; case PM_MaximumDragDistance: return -1; diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp index 707b05e..2d90aa1 100644 --- a/src/gui/styles/qstylesheetstyle.cpp +++ b/src/gui/styles/qstylesheetstyle.cpp @@ -4722,7 +4722,7 @@ int QStyleSheetStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const return subRule.size().height(); else if (subRule.hasBox() || subRule.hasBorder()) { QFontMetrics fm = opt ? opt->fontMetrics : w->fontMetrics(); - return subRule.size(QSize(0, fm.lineSpacing())).height(); + return subRule.size(QSize(0, fm.height())).height(); } break; } diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 1285935..1b4c380 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -2632,7 +2632,7 @@ QFontCache::~QFontCache() while (it != end) { if (--it.value().data->cache_count == 0) { if (it.value().data->ref == 0) { - FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %d %d %d %d)", + FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %g %d %d %d)", it.value().data, it.key().script, it.key().def.pointSize, it.key().def.pixelSize, it.key().def.weight, it.key().def.style, it.key().def.fixedPitch); diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index d74f0b4..144a82d 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -86,7 +86,7 @@ struct QFontDef #endif // Q_WS_X11 qreal pointSize; - int pixelSize; + qreal pixelSize; uint styleStrategy : 16; uint styleHint : 8; diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 738e36a..fb8444e 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -1331,7 +1331,7 @@ static void match(int script, const QFontDef &request, " family: %s [%s], script: %d\n" " weight: %d, style: %d\n" " stretch: %d\n" - " pixelSize: %d\n" + " pixelSize: %g\n" " pitch: %c", family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(), foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index ae26dab..6cde9ed 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qt_windows.h" +#include #include #include "qfont_p.h" #include "qfontengine_p.h" @@ -670,7 +671,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ break; } - lf.lfHeight = -request.pixelSize; + lf.lfHeight = -qRound(request.pixelSize); lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; @@ -899,7 +900,6 @@ static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &r return fe; } - void QFontDatabase::load(const QFontPrivate *d, int script) { // sanity checks @@ -910,8 +910,9 @@ void QFontDatabase::load(const QFontPrivate *d, int script) // normalize the request to get better caching QFontDef req = d->request; if (req.pixelSize <= 0) - req.pixelSize = qMax(1, qRound(req.pointSize * d->dpi / 72.)); - req.pointSize = 0; + req.pixelSize = qreal((req.pointSize * d->dpi) / 72.); + if (req.pixelSize < 1) + req.pixelSize = 1; if (req.weight == 0) req.weight = QFont::Normal; if (req.stretch == 0) @@ -928,7 +929,8 @@ void QFontDatabase::load(const QFontPrivate *d, int script) QFontEngine *fe = QFontCache::instance()->findEngine(key); // set it to the actual pointsize, so QFontInfo will do the right thing - req.pointSize = req.pixelSize*72./d->dpi; + if (req.pointSize < 0) + req.pointSize = req.pixelSize*72./d->dpi; if (!fe) { if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index 27ff003..b582e4a 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -757,7 +758,7 @@ QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &request) double size; if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch) - fontDef.pixelSize = qRound(size); + fontDef.pixelSize = size; else fontDef.pixelSize = 12; @@ -1455,7 +1456,7 @@ void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontD slant_value = FC_SLANT_OBLIQUE; FcPatternAddInteger(pattern, FC_SLANT, slant_value); - double size_value = qMax(1, request.pixelSize); + double size_value = qMax(1., request.pixelSize); FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); int stretch = request.stretch; @@ -1893,8 +1894,9 @@ void QFontDatabase::load(const QFontPrivate *d, int script) // normalize the request to get better caching QFontDef req = d->request; if (req.pixelSize <= 0) - req.pixelSize = qRound(qt_pixelSize(req.pointSize, d->dpi)); - req.pointSize = 0; + req.pixelSize = floor(qt_pixelSize(req.pointSize, d->dpi) * 100 + 0.5) / 100; + if (req.pixelSize < 1) + req.pixelSize = 1; if (req.weight == 0) req.weight = QFont::Normal; if (req.stretch == 0) @@ -1909,7 +1911,9 @@ void QFontDatabase::load(const QFontPrivate *d, int script) return; // set it to the actual pointsize, so QFontInfo will do the right thing - req.pointSize = qt_pointSize(req.pixelSize, d->dpi); + if (req.pointSize < 0) + req.pointSize = qt_pointSize(req.pixelSize, d->dpi); + QFontEngine *fe = QFontCache::instance()->findEngine(key); diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 3da1593..4041717 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -327,7 +327,7 @@ void QFreetypeFace::release(const QFontEngine::FaceId &face_id) void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing) { - *ysize = fontDef.pixelSize << 6; + *ysize = qRound(fontDef.pixelSize * 64); *xsize = *ysize * fontDef.stretch / 100; *outline_drawing = false; @@ -387,7 +387,9 @@ QFontEngine::Properties QFreetypeFace::properties() const p.descent = QFixed::fromFixed(-face->size->metrics.descender); p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); p.emSquare = face->size->metrics.y_ppem; - p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); +// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); + p.boundingBox = QRectF(0, -p.ascent.toReal(), + face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); } p.italicAngle = 0; p.capHeight = p.ascent; @@ -709,6 +711,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) hbFace = freetype->hbFace; metrics = face->size->metrics; + #if defined(Q_WS_QWS) /* TrueType fonts with embedded bitmaps may have a bitmap font specific @@ -1219,7 +1222,8 @@ QFixed QFontEngineFT::ascent() const QFixed QFontEngineFT::descent() const { - return QFixed::fromFixed(-metrics.descender); + // subtract a pixel to work around QFontMetrics's built-in + 1 + return QFixed::fromFixed(-metrics.descender - 64); } QFixed QFontEngineFT::leading() const diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index d781c70..fd34d0f 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -577,7 +577,9 @@ QFixed QFontEngineWin::ascent() const QFixed QFontEngineWin::descent() const { - return tm.tmDescent; + // ### we substract 1 to even out the historical +1 in QFontMetrics's + // ### height=asc+desc+1 equation. Fix in Qt5. + return tm.tmDescent - 1; } QFixed QFontEngineWin::leading() const diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index de83d39..73434b1 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -1437,7 +1437,9 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p option.setTextDirection(dir); layout.setTextOption(option); layout.beginLayout(); - layout.createLine(); + QTextLine line = layout.createLine(); + if (line.isValid()) + line.setLeadingIncluded(true); layout.endLayout(); layout.draw(painter, QPointF(r.left(), pos.y())); break; @@ -2579,6 +2581,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi QTextLine line = tl->createLine(); if (!line.isValid()) break; + line.setLeadingIncluded(true); QFixed left, right; floatMargins(layoutStruct->y, layoutStruct, &left, &right); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index 81c9142..a91408f 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1042,7 +1042,7 @@ void QTextEngine::shapeTextWithCE(int item) const QScriptItem &si = layoutData->items[item]; si.glyph_data_offset = layoutData->used; - QFontEngine *fe = fontEngine(si, &si.ascent, &si.descent); + QFontEngine *fe = fontEngine(si, &si.ascent, &si.descent, &si.leading); QTextEngine::ShaperFlags flags; if (si.analysis.bidiLevel % 2) @@ -1119,7 +1119,7 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const si.glyph_data_offset = layoutData->used; - QFontEngine *font = fontEngine(si, &si.ascent, &si.descent); + QFontEngine *font = fontEngine(si, &si.ascent, &si.descent, &si.leading); bool kerningEnabled = this->font(si).d->kerning; @@ -1350,8 +1350,11 @@ void QTextEngine::shape(int item) const layoutData->items[item].position + block.position(), format); } } else if (layoutData->items[item].analysis.flags == QScriptAnalysis::Tab) { - // set up at least the ascent/descent of the script item for the tab - fontEngine(layoutData->items[item], &layoutData->items[item].ascent, &layoutData->items[item].descent); + // set up at least the ascent/descent/leading of the script item for the tab + fontEngine(layoutData->items[item], + &layoutData->items[item].ascent, + &layoutData->items[item].descent, + &layoutData->items[item].leading); } else { shapeText(item); } @@ -1737,7 +1740,7 @@ QFont QTextEngine::font(const QScriptItem &si) const return font; } -QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent) const +QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent, QFixed *leading) const { QFontEngine *engine = 0; QFontEngine *scaledEngine = 0; @@ -1777,6 +1780,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix if (ascent) { *ascent = engine->ascent(); *descent = engine->descent(); + *leading = engine->leading(); } if (scaledEngine) @@ -2009,8 +2013,12 @@ void QScriptLine::setDefaultHeight(QTextEngine *eng) e = eng->fnt.d->engineForScript(QUnicodeTables::Common); } - ascent = qMax(ascent, e->ascent()); - descent = qMax(descent, e->descent()); + QFixed other_ascent = e->ascent(); + QFixed other_descent = e->descent(); + QFixed other_leading = e->leading(); + leading = qMax(leading + ascent, other_leading + other_ascent) - qMax(ascent, other_ascent); + ascent = qMax(ascent, other_ascent); + descent = qMax(descent, other_descent); } QTextEngine::LayoutData::LayoutData() diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp index e101830..eeccc72 100644 --- a/src/gui/text/qtextengine_mac.cpp +++ b/src/gui/text/qtextengine_mac.cpp @@ -557,7 +557,7 @@ void QTextEngine::shapeTextMac(int item) const si.glyph_data_offset = layoutData->used; - QFontEngine *font = fontEngine(si, &si.ascent, &si.descent); + QFontEngine *font = fontEngine(si, &si.ascent, &si.descent, &si.leading); if (font->type() != QFontEngine::Multi) { shapeTextWithHarfbuzz(item); return; diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 85c6928..a1d363b 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -345,11 +345,11 @@ struct Q_AUTOTEST_EXPORT QScriptItem { inline QScriptItem() : position(0), - num_glyphs(0), descent(-1), ascent(-1), width(-1), + num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1), glyph_data_offset(0) {} inline QScriptItem(int p, const QScriptAnalysis &a) : position(p), analysis(a), - num_glyphs(0), descent(-1), ascent(-1), width(-1), + num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1), glyph_data_offset(0) {} int position; @@ -357,6 +357,7 @@ struct Q_AUTOTEST_EXPORT QScriptItem unsigned short num_glyphs; QFixed descent; QFixed ascent; + QFixed leading; QFixed width; int glyph_data_offset; QFixed height() const { return ascent + descent + 1; } @@ -373,9 +374,10 @@ struct Q_AUTOTEST_EXPORT QScriptLine QScriptLine() : from(0), length(0), justified(0), gridfitted(0), - hasTrailingSpaces(0) {} + hasTrailingSpaces(0), leadingIncluded(0) {} QFixed descent; QFixed ascent; + QFixed leading; QFixed x; QFixed y; QFixed width; @@ -385,7 +387,11 @@ struct Q_AUTOTEST_EXPORT QScriptLine mutable uint justified : 1; mutable uint gridfitted : 1; uint hasTrailingSpaces : 1; - QFixed height() const { return ascent + descent + 1; } + uint leadingIncluded : 1; + QFixed height() const { return ascent + descent + 1 + + (leadingIncluded? qMax(QFixed(),leading) : QFixed()); } + QFixed base() const { return ascent + + (leadingIncluded ? qMax(QFixed(),leading) : QFixed()); } void setDefaultHeight(QTextEngine *eng); void operator+=(const QScriptLine &other); }; @@ -394,6 +400,7 @@ Q_DECLARE_TYPEINFO(QScriptLine, Q_PRIMITIVE_TYPE); inline void QScriptLine::operator+=(const QScriptLine &other) { + leading= qMax(leading + ascent, other.leading + other.ascent) - qMax(ascent, other.ascent); descent = qMax(descent, other.descent); ascent = qMax(ascent, other.ascent); textWidth += other.textWidth; @@ -476,7 +483,7 @@ public: return end - si->position; } - QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0) const; + QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0, QFixed *leading = 0) const; QFont font(const QScriptItem &si) const; inline QFont font() const { return fnt; } diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index c5f0e35..4600a29 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -860,7 +860,7 @@ QRectF QTextLayout::boundingRect() const ymin = qMin(ymin, si.y); xmax = qMax(xmax, si.x+qMax(si.width, si.textWidth)); // ### shouldn't the ascent be used in ymin??? - ymax = qMax(ymax, si.y+si.ascent+si.descent+1); + ymax = qMax(ymax, si.y+si.height()); } return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal()); } @@ -1071,10 +1071,10 @@ static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPo QTextLineItemIterator iterator(eng, lineNumber, pos, selection); - const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent; + + const qreal selectionY = pos.y() + line.y.toReal(); const qreal lineHeight = line.height().toReal(); - const qreal selectionY = (y - line.ascent).toReal(); QFixed lastSelectionX = iterator.x; QFixed lastSelectionWidth; @@ -1334,23 +1334,23 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition const qreal x = position.x() + l.cursorToX(cursorPosition); int itm = d->findItem(cursorPosition - 1); - QFixed ascent = sl.ascent; + QFixed base = sl.base(); QFixed descent = sl.descent; bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft); if (itm >= 0) { const QScriptItem &si = d->layoutData->items.at(itm); if (si.ascent > 0) - ascent = si.ascent; + base = si.ascent; if (si.descent > 0) descent = si.descent; rightToLeft = si.analysis.bidiLevel % 2; } - qreal y = position.y() + (sl.y + sl.ascent - ascent).toReal(); + qreal y = position.y() + (sl.y + sl.base() - base).toReal(); bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) && (p->transform().type() > QTransform::TxTranslate); if (toggleAntialiasing) p->setRenderHint(QPainter::Antialiasing); - p->fillRect(QRectF(x, y, qreal(width), (ascent + descent).toReal()), p->pen().brush()); + p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush()); if (toggleAntialiasing) p->setRenderHint(QPainter::Antialiasing, false); if (d->layoutData->hasBidi) { @@ -1500,9 +1500,11 @@ qreal QTextLine::descent() const } /*! - Returns the line's height. This is equal to ascent() + descent() + 1. + Returns the line's height. This is equal to ascent() + descent() + 1 + if leading is not included. If leading is included, this equals to + ascent() + descent() + leading() + 1. - \sa ascent() descent() + \sa ascent() descent() leading() setLeadingIncluded() */ qreal QTextLine::height() const { @@ -1510,6 +1512,51 @@ qreal QTextLine::height() const } /*! + \since 4.6 + + Returns the line's leading. + + \sa ascent() descent() height() +*/ +qreal QTextLine::leading() const +{ + return eng->lines[i].leading.toReal(); +} + +/*! \since 4.6 + + Includes positive leading into the line's height if \a included is true; + otherwise does not include leading. + + By default, leading is not included. + + Note that negative leading is ignored, it must be handled + in the code using the text lines by letting the lines overlap. + + \sa leadingIncluded() + +*/ +void QTextLine::setLeadingIncluded(bool included) +{ + eng->lines[i].leadingIncluded= included; + +} + +/*! \since 4.6 + + Returns true if positive leading is included into the line's height; otherwise returns false. + + By default, leading is not included. + + \sa setLeadingIncluded() +*/ +bool QTextLine::leadingIncluded() const +{ + return eng->lines[i].leadingIncluded; +} + + +/*! Returns the width of the line that is occupied by text. This is always \<= to width(), and is the minimum width that could be used by layout() without changing the line break position. @@ -1712,6 +1759,9 @@ void QTextLine::layout_helper(int maxGlyphs) } const QScriptItem ¤t = eng->layoutData->items[item]; + lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent, + current.leading + current.ascent) - qMax(lbh.tmpData.ascent, + current.ascent); lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent); lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent); @@ -2042,7 +2092,9 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR QTextLineItemIterator iterator(eng, i, pos, selection); - const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent; + QFixed lineBase = line.base(); + + const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase; bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors); while (!iterator.atEnd()) { @@ -2065,7 +2117,7 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR if (selection) format.merge(selection->format); - setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - line.ascent).toReal(), + setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(), iterator.itemWidth.toReal(), line.height().toReal())); QTextCharFormat::VerticalAlignment valign = format.verticalAlignment(); @@ -2086,7 +2138,7 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) { QFixed itemY = y - si.ascent; if (format.verticalAlignment() == QTextCharFormat::AlignTop) { - itemY = y - line.ascent; + itemY = y - lineBase; } QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal()); diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h index 90afac8..9f170f5 100644 --- a/src/gui/text/qtextlayout.h +++ b/src/gui/text/qtextlayout.h @@ -196,6 +196,10 @@ public: qreal ascent() const; qreal descent() const; qreal height() const; + qreal leading() const; + + void setLeadingIncluded(bool included); + bool leadingIncluded() const; qreal naturalTextWidth() const; QRectF naturalTextRect() const; diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index b28ecd7..b7615a4 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -78,6 +78,7 @@ win32 { unix:x11 { HEADERS += \ text/qfontengine_x11_p.h \ + text/qfontdatabase_x11.cpp \ text/qfontengine_ft_p.h SOURCES += \ text/qfont_x11.cpp \ diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 0e888d6..05e1e74 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -314,7 +314,7 @@ QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh) const // height - sh.setHeight(qMax(fm.lineSpacing(), 14) + 2); + sh.setHeight(qMax(fm.height(), 14) + 2); if (hasIcon) { sh.setHeight(qMax(sh.height(), iconSize.height() + 2)); } diff --git a/src/gui/widgets/qdockwidget.cpp b/src/gui/widgets/qdockwidget.cpp index a574262f..6710275 100644 --- a/src/gui/widgets/qdockwidget.cpp +++ b/src/gui/widgets/qdockwidget.cpp @@ -456,7 +456,7 @@ int QDockWidgetLayout::titleHeight() const int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); - return qMax(buttonHeight + 2, titleFontMetrics.lineSpacing() + 2*mw); + return qMax(buttonHeight + 2, titleFontMetrics.height() + 2*mw); } void QDockWidgetLayout::setGeometry(const QRect &geometry) diff --git a/src/gui/widgets/qfontcombobox.cpp b/src/gui/widgets/qfontcombobox.cpp index 7b39823..806db59 100644 --- a/src/gui/widgets/qfontcombobox.cpp +++ b/src/gui/widgets/qfontcombobox.cpp @@ -194,7 +194,7 @@ QSize QFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option, // font.setFamily(text); font.setPointSize(QFontInfo(font).pointSize() * 3/2); QFontMetrics fontMetrics(font); - return QSize(fontMetrics.width(text), fontMetrics.lineSpacing()); + return QSize(fontMetrics.width(text), fontMetrics.height()); } diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp index 629e839..e4252b5 100644 --- a/src/gui/widgets/qlineedit.cpp +++ b/src/gui/widgets/qlineedit.cpp @@ -624,7 +624,7 @@ QSize QLineEdit::sizeHint() const Q_D(const QLineEdit); ensurePolished(); QFontMetrics fm(font()); - int h = qMax(fm.lineSpacing(), 14) + 2*d->verticalMargin + int h = qMax(fm.height(), 14) + 2*d->verticalMargin + d->topTextMargin + d->bottomTextMargin + d->topmargin + d->bottommargin; int w = fm.width(QLatin1Char('x')) * 17 + 2*d->horizontalMargin diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp index 22438bf..fc61889 100644 --- a/src/gui/widgets/qplaintextedit.cpp +++ b/src/gui/widgets/qplaintextedit.cpp @@ -357,10 +357,8 @@ void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block) Q_D(QPlainTextDocumentLayout); QTextDocument *doc = document(); qreal margin = doc->documentMargin(); - QFontMetrics fm(doc->defaultFont()); qreal blockMaximumWidth = 0; - int leading = qMax(0, fm.leading()); qreal height = 0; QTextLayout *tl = block.layout(); QTextOption option = doc->defaultTextOption(); @@ -381,9 +379,8 @@ void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block) QTextLine line = tl->createLine(); if (!line.isValid()) break; + line.setLeadingIncluded(true); line.setLineWidth(availableWidth); - - height += leading; line.setPosition(QPointF(margin, height)); height += line.height(); blockMaximumWidth = qMax(blockMaximumWidth, line.naturalTextWidth() + 2*margin); -- cgit v0.12