diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/text/qglyphrun.cpp | 45 | ||||
-rw-r--r-- | src/gui/text/qglyphrun.h | 3 | ||||
-rw-r--r-- | src/gui/text/qglyphrun_p.h | 2 | ||||
-rw-r--r-- | src/gui/text/qtextcontrol.cpp | 24 | ||||
-rw-r--r-- | src/gui/text/qtextcontrol_p.h | 3 | ||||
-rw-r--r-- | src/gui/text/qtextlayout.cpp | 63 | ||||
-rw-r--r-- | src/gui/text/qtextobject.cpp | 17 | ||||
-rw-r--r-- | src/gui/text/qtextobject.h | 2 | ||||
-rw-r--r-- | src/gui/widgets/qlinecontrol.cpp | 43 | ||||
-rw-r--r-- | src/gui/widgets/qlinecontrol_p.h | 33 |
10 files changed, 196 insertions, 39 deletions
diff --git a/src/gui/text/qglyphrun.cpp b/src/gui/text/qglyphrun.cpp index cc825525c4..be9c058693 100644 --- a/src/gui/text/qglyphrun.cpp +++ b/src/gui/text/qglyphrun.cpp @@ -45,6 +45,7 @@ #include "qglyphrun.h" #include "qglyphrun_p.h" +#include <qdebug.h> QT_BEGIN_NAMESPACE @@ -343,12 +344,44 @@ void QGlyphRun::setStrikeOut(bool strikeOut) } /*! - Returns the smallest rectangle that contains all glyphs in this QGlyphRun. + Sets the bounding rect of the glyphs in this QGlyphRun to be \a boundingRect. This rectangle + will be returned by boundingRect() unless it is empty, in which case the bounding rectangle of the + glyphs in the glyph run will be returned instead. + + \note Unless you are implementing text shaping, you should not have to use this function. + It is used specifically when the QGlyphRun should represent an area which is smaller than the + area of the glyphs it contains. This could happen e.g. if the glyph run is retrieved by calling + QTextLayout::glyphRuns() and the specified range only includes part of a ligature (where two or + more characters are combined to a single glyph.) When this is the case, the bounding rect should + only include the appropriate part of the ligature glyph, based on a calculation of the average + width of the characters in the ligature. + + In order to support such a case (an example is selections which should be drawn with a different + color than the main text color), it is necessary to clip the painting mechanism to the rectangle + returned from boundingRect() to avoid drawing the entire ligature glyph. + + \sa boundingRect() + + \since 5.0 +*/ +void QGlyphRun::setBoundingRect(const QRectF &boundingRect) +{ + detach(); + d->boundingRect = boundingRect; +} + +/*! + Returns the smallest rectangle that contains all glyphs in this QGlyphRun. If a bounding rect + has been set using setBoundingRect(), then this will be returned. Otherwise the bounding rect + will be calculated based on the font metrics of the glyphs in the glyph run. \since 5.0 */ QRectF QGlyphRun::boundingRect() const { + if (!d->boundingRect.isEmpty()) + return d->boundingRect; + qreal minX, minY, maxX, maxY; for (int i=0; i<qMin(d->glyphPositions.size(), d->glyphIndexes.size()); ++i) { @@ -371,6 +404,16 @@ QRectF QGlyphRun::boundingRect() const return QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); } +/*! + Returns true if the QGlyphRun does not contain any glyphs. + + \since 5.0 +*/ +bool QGlyphRun::isEmpty() const +{ + return d->glyphIndexes.isEmpty(); +} + QT_END_NAMESPACE #endif // QT_NO_RAWFONT diff --git a/src/gui/text/qglyphrun.h b/src/gui/text/qglyphrun.h index b4f02f0d87..da88bc72f9 100644 --- a/src/gui/text/qglyphrun.h +++ b/src/gui/text/qglyphrun.h @@ -91,8 +91,11 @@ public: void setStrikeOut(bool strikeOut); bool strikeOut() const; + void setBoundingRect(const QRectF &boundingRect); QRectF boundingRect() const; + bool isEmpty() const; + private: friend class QGlyphRunPrivate; friend class QTextLine; diff --git a/src/gui/text/qglyphrun_p.h b/src/gui/text/qglyphrun_p.h index a7745e68ce..b632678971 100644 --- a/src/gui/text/qglyphrun_p.h +++ b/src/gui/text/qglyphrun_p.h @@ -83,6 +83,7 @@ public: , glyphIndexes(other.glyphIndexes) , glyphPositions(other.glyphPositions) , rawFont(other.rawFont) + , boundingRect(other.boundingRect) , overline(other.overline) , underline(other.underline) , strikeOut(other.strikeOut) @@ -96,6 +97,7 @@ public: QVector<quint32> glyphIndexes; QVector<QPointF> glyphPositions; QRawFont rawFont; + QRectF boundingRect; uint overline : 1; uint underline : 1; diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp index 424d1979b2..c29379ed28 100644 --- a/src/gui/text/qtextcontrol.cpp +++ b/src/gui/text/qtextcontrol.cpp @@ -409,6 +409,8 @@ void QTextControlPrivate::init(Qt::TextFormat format, const QString &text, QText doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable); q->setCursorWidth(-1); + + QObject::connect(q, SIGNAL(updateCursorRequest(QRectF)), q, SIGNAL(updateRequest(QRectF))); } void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document) @@ -547,7 +549,7 @@ void QTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode) void QTextControlPrivate::repaintCursor() { Q_Q(QTextControl); - emit q->updateRequest(cursorRectPlusUnicodeDirectionMarkers(cursor)); + emit q->updateCursorRequest(cursorRectPlusUnicodeDirectionMarkers(cursor)); } void QTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection) @@ -565,9 +567,16 @@ void QTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelect differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor); emit q->updateRequest(q->selectionRect(differenceSelection)); } else { - if (!oldSelection.isNull()) - emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection)); - emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor)); + if (!oldSelection.hasSelection() && !cursor.hasSelection()) { + if (!oldSelection.isNull()) + emit q->updateCursorRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection)); + emit q->updateCursorRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor)); + + } else { + if (!oldSelection.isNull()) + emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection)); + emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor)); + } } } @@ -2959,6 +2968,12 @@ void QTextControl::setPalette(const QPalette &pal) d->palette = pal; } +bool QTextControl::cursorOn() const +{ + Q_D(const QTextControl); + return d->cursorOn; +} + QAbstractTextDocumentLayout::PaintContext QTextControl::getPaintContext(QWidget *widget) const { Q_D(const QTextControl); @@ -3146,6 +3161,7 @@ void QTextEditMimeData::setup() const fragment = QTextDocumentFragment(); } + QT_END_NAMESPACE #include "moc_qtextcontrol_p.cpp" diff --git a/src/gui/text/qtextcontrol_p.h b/src/gui/text/qtextcontrol_p.h index cbf26d2122..c5ed0ee1e0 100644 --- a/src/gui/text/qtextcontrol_p.h +++ b/src/gui/text/qtextcontrol_p.h @@ -226,6 +226,7 @@ Q_SIGNALS: void cursorPositionChanged(); // control signals + void updateCursorRequest(const QRectF &rect = QRectF()); void updateRequest(const QRectF &rect = QRectF()); void documentSizeChanged(const QSizeF &); void blockCountChanged(int newBlockCount); @@ -258,6 +259,8 @@ public: bool setFocusToNextOrPreviousAnchor(bool next); bool findNextPrevAnchor(const QTextCursor& from, bool next, QTextCursor& newAnchor); + bool cursorOn() const; + protected: virtual void timerEvent(QTimerEvent *e); diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 2535faddc3..8c5e63da54 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -42,6 +42,7 @@ #include "qtextlayout.h" #include "qtextengine_p.h" +#include <qthread.h> #include <qfont.h> #include <qapplication.h> #include <qpainter.h> @@ -2108,8 +2109,10 @@ static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const Q } +#if !defined(QT_NO_RAWFONT) static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &glyphLayout, - const QPointF &pos, const QTextItem::RenderFlags &flags) + const QPointF &pos, const QTextItem::RenderFlags &flags, + const QFixed &selectionX, const QFixed &selectionWidth) { QGlyphRun glyphRun; @@ -2117,6 +2120,7 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &g QRawFont font; QRawFontPrivate *fontD = QRawFontPrivate::get(font); fontD->fontEngine = fontEngine; + fontD->thread = QThread::currentThread(); fontD->fontEngine->ref.ref(); #if defined(Q_WS_WIN) @@ -2151,13 +2155,27 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &g positionsArray); Q_ASSERT(glyphsArray.size() == positionsArray.size()); + qreal fontHeight = font.ascent() + font.descent(); + qreal minY; + qreal maxY; QVector<quint32> glyphs; QVector<QPointF> positions; for (int i=0; i<glyphsArray.size(); ++i) { glyphs.append(glyphsArray.at(i) & 0xffffff); - positions.append(positionsArray.at(i).toPointF() + pos); + + QPointF position = positionsArray.at(i).toPointF() + pos; + positions.append(position); + + if (i == 0) { + maxY = minY = position.y(); + } else { + minY = qMin(minY, position.y()); + maxY = qMax(maxY, position.y()); + } } + qreal height = maxY + fontHeight - minY; + glyphRun.setGlyphIndexes(glyphs); glyphRun.setPositions(positions); @@ -2166,6 +2184,8 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &g glyphRun.setStrikeOut(flags.testFlag(QTextItem::StrikeOut)); glyphRun.setRawFont(font); + glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY, selectionWidth.toReal(), height)); + return glyphRun; } @@ -2182,7 +2202,6 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &g \sa QTextLayout::glyphRuns() */ -#if !defined(QT_NO_RAWFONT) QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const { const QScriptLine &line = eng->lines[i]; @@ -2196,7 +2215,14 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const if (length < 0) length = textLength(); - QTextLineItemIterator iterator(eng, i); + if (length == 0) + return QList<QGlyphRun>(); + + QTextLayout::FormatRange selection; + selection.start = from; + selection.length = length; + + QTextLineItemIterator iterator(eng, i, QPointF(), &selection); qreal y = line.y.toReal() + line.base().toReal(); QList<QGlyphRun> glyphRuns; while (!iterator.atEnd()) { @@ -2206,7 +2232,10 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const QPointF pos(iterator.x.toReal(), y); if (from >= 0 && length >= 0 && - (from >= si.position + eng->length(&si) || from + length <= si.position)) { + (from >= si.position + eng->length(&si) + || from + length <= si.position + || from + length <= iterator.itemStart + || from >= iterator.itemEnd)) { continue; } @@ -2235,19 +2264,22 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const ? si.num_glyphs - 1 : logClusters[relativeTo]; + int itemGlyphsStart = logClusters[iterator.itemStart - si.position]; + int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position]; + QGlyphLayout glyphLayout = eng->shapedGlyphs(&si); // Calculate new x position of glyph layout for a subset. This becomes somewhat complex // when we're breaking a RTL script item, since the expected position passed into // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run. if (relativeFrom != (iterator.itemStart - si.position) && !rtl) { - for (int i=0; i<glyphsStart; ++i) { + for (int i=itemGlyphsStart; i<glyphsStart; ++i) { QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(), glyphLayout.advances_y[i].toReal()); } } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) { - for (int i=glyphLayout.numGlyphs - 1; i>glyphsEnd; --i) { + for (int i=itemGlyphsEnd; i>glyphsEnd; --i) { QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(), glyphLayout.advances_y[i].toReal()); @@ -2256,6 +2288,10 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1); + QFixed x; + QFixed width; + iterator.getSelectionBounds(&x, &width); + if (glyphLayout.numGlyphs > 0) { QFontEngine *mainFontEngine = font.d->engineForScript(si.analysis.script); if (mainFontEngine->type() == QFontEngine::Multi) { @@ -2270,7 +2306,7 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const QGlyphLayout subLayout = glyphLayout.mid(start, end - start); glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which), - subLayout, pos, flags)); + subLayout, pos, flags, x, width)); for (int i = 0; i < subLayout.numGlyphs; i++) { pos += QPointF(subLayout.advances_x[i].toReal(), subLayout.advances_y[i].toReal()); @@ -2281,10 +2317,15 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const } QGlyphLayout subLayout = glyphLayout.mid(start, end - start); - glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which), - subLayout, pos, flags)); + QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which), + subLayout, pos, flags, x, width); + if (!glyphRun.isEmpty()) + glyphRuns.append(glyphRun); } else { - glyphRuns.append(glyphRunWithInfo(mainFontEngine, glyphLayout, pos, flags)); + QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine, glyphLayout, pos, flags, x, + width); + if (!glyphRun.isEmpty()) + glyphRuns.append(glyphRun); } } } diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp index cea5eac465..d641266367 100644 --- a/src/gui/text/qtextobject.cpp +++ b/src/gui/text/qtextobject.cpp @@ -1667,21 +1667,24 @@ QTextBlock::iterator &QTextBlock::iterator::operator--() \sa QGlyphRun, QTextBlock::layout(), QTextLayout::position(), QPainter::drawGlyphRun() */ #if !defined(QT_NO_RAWFONT) -QList<QGlyphRun> QTextFragment::glyphRuns() const +QList<QGlyphRun> QTextFragment::glyphRuns(int pos, int len) const { if (!p || !n) return QList<QGlyphRun>(); - int pos = position(); - int len = length(); - if (len == 0) - return QList<QGlyphRun>(); - - int blockNode = p->blockMap().findNode(pos); + int blockNode = p->blockMap().findNode(position()); const QTextBlockData *blockData = p->blockMap().fragment(blockNode); QTextLayout *layout = blockData->layout; + int blockPosition = p->blockMap().position(blockNode); + if (pos < 0) + pos = position() - blockPosition; + if (len < 0) + len = length(); + if (len == 0) + return QList<QGlyphRun>(); + QList<QGlyphRun> ret; for (int i=0; i<layout->lineCount(); ++i) { QTextLine textLine = layout->lineAt(i); diff --git a/src/gui/text/qtextobject.h b/src/gui/text/qtextobject.h index 9c5cc13539..c2b46e4d12 100644 --- a/src/gui/text/qtextobject.h +++ b/src/gui/text/qtextobject.h @@ -317,7 +317,7 @@ public: QString text() const; #if !defined(QT_NO_RAWFONT) - QList<QGlyphRun> glyphRuns() const; + QList<QGlyphRun> glyphRuns(int from = -1, int length = -1) const; #endif private: diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp index 92c84d72e0..9b7d9b83ad 100644 --- a/src/gui/widgets/qlinecontrol.cpp +++ b/src/gui/widgets/qlinecontrol.cpp @@ -76,6 +76,28 @@ static int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY; */ /*! + \internal + + Updates the internal text layout. Returns the ascent of the + created QTextLine. +*/ +int QLineControl::redoTextLayout() const +{ + m_textLayout.clearLayout(); + + m_textLayout.beginLayout(); + QTextLine l = m_textLayout.createLine(); + m_textLayout.endLayout(); + +#if defined(Q_WS_MAC) + if (m_threadChecks) + m_textLayoutThread = QThread::currentThread(); +#endif + + return qRound(l.ascent()); +} + +/*! \internal Updates the display text based of the current edit text @@ -124,15 +146,12 @@ void QLineControl::updateDisplayText(bool forceUpdate) m_textLayout.setText(str); - QTextOption option; + QTextOption option = m_textLayout.textOption(); option.setTextDirection(m_layoutDirection); option.setFlags(QTextOption::IncludeTrailingSpaces); m_textLayout.setTextOption(option); - m_textLayout.beginLayout(); - QTextLine l = m_textLayout.createLine(); - m_textLayout.endLayout(); - m_ascent = qRound(l.ascent()); + m_ascent = redoTextLayout(); if (str != orig || forceUpdate) emit displayTextChanged(str); @@ -228,7 +247,7 @@ void QLineControl::del() if (hasSelectedText()) { removeSelectedText(); } else { - int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor; + int n = textLayout()->nextCursorPosition(m_cursor) - m_cursor; while (n--) internalDelete(); } @@ -357,7 +376,7 @@ void QLineControl::updatePasswordEchoEditing(bool editing) */ int QLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const { - return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn); + return textLayout()->lineAt(0).xToCursor(x, betweenOrOn); } /*! @@ -368,7 +387,7 @@ int QLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const */ QRect QLineControl::cursorRect() const { - QTextLine l = m_textLayout.lineAt(0); + QTextLine l = textLayout()->lineAt(0); int c = m_cursor; if (m_preeditCursor != -1) c += m_preeditCursor; @@ -578,14 +597,14 @@ void QLineControl::draw(QPainter *painter, const QPoint &offset, const QRect &cl } if (flags & DrawText) - m_textLayout.draw(painter, offset, selections, clip); + textLayout()->draw(painter, offset, selections, clip); if (flags & DrawCursor){ int cursor = m_cursor; if (m_preeditCursor != -1) cursor += m_preeditCursor; if (!m_hideCursor && (!m_blinkPeriod || m_blinkStatus)) - m_textLayout.drawCursor(painter, offset, cursor, m_cursorWidth); + textLayout()->drawCursor(painter, offset, cursor, m_cursorWidth); } } @@ -601,10 +620,10 @@ void QLineControl::selectWordAtPos(int cursor) int next = cursor + 1; if(next > end()) --next; - int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords); + int c = textLayout()->previousCursorPosition(next, QTextLayout::SkipWords); moveCursor(c, false); // ## text layout should support end of words. - int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords); + int end = textLayout()->nextCursorPosition(c, QTextLayout::SkipWords); while (end > cursor && m_text[end-1].isSpace()) --end; moveCursor(end, true); diff --git a/src/gui/widgets/qlinecontrol_p.h b/src/gui/widgets/qlinecontrol_p.h index 44bd7214be..d4c4154b3d 100644 --- a/src/gui/widgets/qlinecontrol_p.h +++ b/src/gui/widgets/qlinecontrol_p.h @@ -65,6 +65,7 @@ #include "QtCore/qpoint.h" #include "QtGui/qcompleter.h" #include "QtGui/qaccessible.h" +#include "QtCore/qthread.h" #include "qplatformdefs.h" @@ -90,6 +91,10 @@ public: #ifdef QT_GUI_PASSWORD_ECHO_DELAY , m_passwordEchoTimer(0) #endif +#if defined(Q_WS_MAC) + , m_threadChecks(false) + , m_textLayoutThread(0) + #endif { init(txt); } @@ -332,11 +337,27 @@ public: bool processEvent(QEvent *ev); - QTextLayout *textLayout() + QTextLayout *textLayout() const { +#if defined(Q_WS_MAC) + if (m_threadChecks && QThread::currentThread() != m_textLayoutThread) + redoTextLayout(); +#endif return &m_textLayout; } +#if defined(Q_WS_MAC) + void setThreadChecks(bool threadChecks) + { + m_threadChecks = threadChecks; + } + + bool threadChecks() const + { + return m_threadChecks; + } +#endif + private: void init(const QString &txt); void removeSelectedText(); @@ -433,8 +454,8 @@ private: QString stripString(const QString &str) const; int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const; - // complex text layout - QTextLayout m_textLayout; + // complex text layout (must be mutable so it can be reshaped at will) + mutable QTextLayout m_textLayout; bool m_passwordEchoEditing; QChar m_passwordCharacter; @@ -451,6 +472,12 @@ private: #endif } + int redoTextLayout() const; +#if defined(Q_WS_MAC) + bool m_threadChecks; + mutable QThread *m_textLayoutThread; +#endif + Q_SIGNALS: void cursorPositionChanged(int, int); void selectionChanged(); |