diff options
-rw-r--r-- | src/declarative/graphicsitems/qmlgraphicstext.cpp | 57 | ||||
-rw-r--r-- | src/declarative/graphicsitems/qmlgraphicstext_p.h | 3 | ||||
-rw-r--r-- | src/declarative/graphicsitems/qmlgraphicstext_p_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qpodvector_p.h | 4 | ||||
-rw-r--r-- | src/declarative/util/qmlstyledtext.cpp | 356 | ||||
-rw-r--r-- | src/declarative/util/qmlstyledtext_p.h | 63 | ||||
-rw-r--r-- | src/declarative/util/util.pri | 6 |
7 files changed, 465 insertions, 25 deletions
diff --git a/src/declarative/graphicsitems/qmlgraphicstext.cpp b/src/declarative/graphicsitems/qmlgraphicstext.cpp index 459541e..ce86b8f 100644 --- a/src/declarative/graphicsitems/qmlgraphicstext.cpp +++ b/src/declarative/graphicsitems/qmlgraphicstext.cpp @@ -41,6 +41,7 @@ #include "qmlgraphicstext_p.h" #include "qmlgraphicstext_p_p.h" +#include <qmlstyledtext_p.h> #include <qfxperf_p_p.h> @@ -143,7 +144,7 @@ void QmlGraphicsText::setFont(const QFont &font) Q_D(QmlGraphicsText); d->font = font; - d->updateSize(); + d->updateLayout(); d->markImgDirty(); } @@ -166,7 +167,7 @@ void QmlGraphicsText::setText(const QString &n) } d->text = n; - d->updateSize(); + d->updateLayout(); d->markImgDirty(); emit textChanged(d->text); } @@ -336,7 +337,7 @@ void QmlGraphicsText::setWrap(bool w) d->wrap = w; - d->updateSize(); + d->updateLayout(); d->markImgDirty(); } @@ -387,12 +388,13 @@ void QmlGraphicsText::setTextFormat(TextFormat format) Q_D(QmlGraphicsText); if (format == d->format) return; + d->format = format; bool wasRich = d->richText; d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); if (wasRich && !d->richText) { //### delete control? (and vice-versa below) - d->updateSize(); + d->updateLayout(); d->markImgDirty(); } else if (!wasRich && d->richText) { if (!d->doc) { @@ -400,10 +402,9 @@ void QmlGraphicsText::setTextFormat(TextFormat format) d->doc->setDocumentMargin(0); } d->doc->setHtml(d->text); - d->updateSize(); + d->updateLayout(); d->markImgDirty(); } - d->format = format; } /*! @@ -436,7 +437,7 @@ void QmlGraphicsText::setElideMode(QmlGraphicsText::TextElideMode mode) d->elideMode = mode; - d->updateSize(); + d->updateLayout(); d->markImgDirty(); } @@ -453,6 +454,34 @@ void QmlGraphicsText::geometryChanged(const QRectF &newGeometry, QmlGraphicsItem::geometryChanged(newGeometry, oldGeometry); } +void QmlGraphicsTextPrivate::updateLayout() +{ + Q_Q(QmlGraphicsText); + if (q->isComponentComplete()) { + //setup instance of QTextLayout for all cases other than richtext + if (!richText) { + layout.clearLayout(); + layout.setFont(font); + if (format != QmlGraphicsText::StyledText) { + QString tmp = text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + singleline = !tmp.contains(QChar::LineSeparator); + if (singleline && elideMode != QmlGraphicsText::ElideNone && q->widthValid()) { + QFontMetrics fm(font); + tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); // XXX still worth layout...? + } + layout.setText(tmp); + } else { + singleline = false; + QmlStyledText::parse(text, layout); + } + } + updateSize(); + } else { + dirty = true; + } +} + void QmlGraphicsTextPrivate::updateSize() { Q_Q(QmlGraphicsText); @@ -464,20 +493,10 @@ void QmlGraphicsTextPrivate::updateSize() } int dy = q->height(); - QString tmp; QSize size(0, 0); //setup instance of QTextLayout for all cases other than richtext - if (!richText) - { - tmp = text; - tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); - singleline = !tmp.contains(QChar::LineSeparator); - if (singleline && elideMode != QmlGraphicsText::ElideNone && q->widthValid()) - tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); // XXX still worth layout...? - layout.clearLayout(); - layout.setFont(font); - layout.setText(tmp); + if (!richText) { size = setupTextLayout(&layout); cachedLayoutSize = size; dy -= size.height(); @@ -769,7 +788,7 @@ void QmlGraphicsText::componentComplete() #endif QmlGraphicsItem::componentComplete(); if (d->dirty) { - d->updateSize(); + d->updateLayout(); d->dirty = false; } } diff --git a/src/declarative/graphicsitems/qmlgraphicstext_p.h b/src/declarative/graphicsitems/qmlgraphicstext_p.h index 6ca460b..717fcb4 100644 --- a/src/declarative/graphicsitems/qmlgraphicstext_p.h +++ b/src/declarative/graphicsitems/qmlgraphicstext_p.h @@ -86,7 +86,8 @@ public: Sunken }; enum TextFormat { PlainText = Qt::PlainText, RichText = Qt::RichText, - AutoText = Qt::AutoText }; + AutoText = Qt::AutoText, + StyledText = 4 }; enum TextElideMode { ElideLeft = Qt::ElideLeft, ElideRight = Qt::ElideRight, ElideMiddle = Qt::ElideMiddle, diff --git a/src/declarative/graphicsitems/qmlgraphicstext_p_p.h b/src/declarative/graphicsitems/qmlgraphicstext_p_p.h index 79d1a4f..73c3dd7 100644 --- a/src/declarative/graphicsitems/qmlgraphicstext_p_p.h +++ b/src/declarative/graphicsitems/qmlgraphicstext_p_p.h @@ -80,6 +80,7 @@ public: ~QmlGraphicsTextPrivate(); void updateSize(); + void updateLayout(); void markImgDirty() { Q_Q(QmlGraphicsText); imgDirty = true; diff --git a/src/declarative/qml/qpodvector_p.h b/src/declarative/qml/qpodvector_p.h index add6e98..42d6017 100644 --- a/src/declarative/qml/qpodvector_p.h +++ b/src/declarative/qml/qpodvector_p.h @@ -92,10 +92,8 @@ public: m_data = (T *)realloc(m_data, m_capacity * sizeof(T)); } int moveCount = m_count - idx; - if (moveCount) { - qDebug() << "insert" << m_count << idx; + if (moveCount) ::memmove(m_data + idx + 1, m_data + idx, moveCount * sizeof(T)); - } m_count++; m_data[idx] = v; } diff --git a/src/declarative/util/qmlstyledtext.cpp b/src/declarative/util/qmlstyledtext.cpp new file mode 100644 index 0000000..ce2b2ea --- /dev/null +++ b/src/declarative/util/qmlstyledtext.cpp @@ -0,0 +1,356 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QXmlStreamReader> +#include <QStack> +#include <QVector> +#include <QPainter> +#include <QTextLayout> +#include <QDebug> +#include <qmath.h> +#include "qmlstyledtext_p.h" + +/* + QmlStyledText supports few tags: + + <b></b> - bold + <br> - new line + <font color="color_name"></font> + + The opening and closing tags may be correctly nested. +*/ + +class QmlStyledTextPrivate +{ +public: + QmlStyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l) {} + + void parse(); + bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format); + bool parseCloseTag(const QChar *&ch, const QString &textIn); + bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); + QPair<QStringRef,QStringRef> parseAttribute(const QChar *&ch, const QString &textIn); + QStringRef parseValue(const QChar *&ch, const QString &textIn); + + inline void skipSpace(const QChar *&ch) { + while (ch->isSpace() && !ch->isNull()) + ++ch; + } + + QString text; + QTextLayout &layout; + + static const QChar lessThan; + static const QChar greaterThan; + static const QChar equals; + static const QChar singleQuote; + static const QChar doubleQuote; + static const QChar slash; +}; + +const QChar QmlStyledTextPrivate::lessThan(QLatin1Char('<')); +const QChar QmlStyledTextPrivate::greaterThan(QLatin1Char('>')); +const QChar QmlStyledTextPrivate::equals(QLatin1Char('=')); +const QChar QmlStyledTextPrivate::singleQuote(QLatin1Char('\'')); +const QChar QmlStyledTextPrivate::doubleQuote(QLatin1Char('\"')); +const QChar QmlStyledTextPrivate::slash(QLatin1Char('/')); + +QmlStyledText::QmlStyledText(const QString &string, QTextLayout &layout) +: d(new QmlStyledTextPrivate(string, layout)) +{ +} + +QmlStyledText::~QmlStyledText() +{ + delete d; +} + +void QmlStyledText::parse(const QString &string, QTextLayout &layout) +{ + QmlStyledText styledText(string, layout); + styledText.d->parse(); +} + +#define CUSTOM_PARSER + +#ifndef CUSTOM_PARSER +void QmlStyledTextPrivate::parse() +{ + QList<QTextLayout::FormatRange> ranges; + QStack<QTextCharFormat> formatStack; + + QString drawText; + drawText.reserve(text.count()); + + QXmlStreamReader xml("<html>" + text + "</html>"); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.tokenType() == QXmlStreamReader::StartElement) { + QStringRef name = xml.name(); + if (name == "b") { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + else + format.setFont(layout.font()); + format.setFontWeight(QFont::Bold); + formatStack.push(format); + } else if (name == "br") { + drawText.append(QChar(QChar::LineSeparator)); + } else if (name == "font") { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + else + format.setFont(layout.font()); + QStringRef col = xml.attributes().value("color"); + if (!col.isEmpty()) { + format.setForeground(QColor(col.toString())); + formatStack.push(format); + } + } + } else if (xml.tokenType() == QXmlStreamReader::EndElement) { + if (formatStack.count() > 1) { + QStringRef name = xml.name(); + if (name == "b" || name == "font" || name == "br") + formatStack.pop(); + } + } else if (xml.tokenType() == QXmlStreamReader::Characters) { + if (formatStack.count() > 1) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = drawText.length(); + formatRange.length = xml.text().length(); + ranges.append(formatRange); + } + drawText.append(xml.text()); + } + } + + layout.setText(drawText); + layout.setAdditionalFormats(ranges); +} +#else +void QmlStyledTextPrivate::parse() +{ + QList<QTextLayout::FormatRange> ranges; + QStack<QTextCharFormat> formatStack; + + QString drawText; + drawText.reserve(text.count()); + + int textStart = 0; + int textLength = 0; + const QChar *ch = text.constData(); + while (!ch->isNull()) { + if (*ch == lessThan) { + if (textLength) { + if (formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = drawText.length(); + formatRange.length = textLength; + ranges.append(formatRange); + } + drawText.append(QStringRef(&text, textStart, textLength)); + } + ++ch; + if (*ch == slash) { + ++ch; + if (parseCloseTag(ch, text)) + formatStack.pop(); + } else { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + else + format.setFont(layout.font()); + if (parseTag(ch, text, drawText, format)) + formatStack.push(format); + } + textStart = ch - text.constData() + 1; + textLength = 0; + } else { + ++textLength; + } + ++ch; + } + if (textLength) { + if (formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = drawText.length(); + formatRange.length = textLength; + ranges.append(formatRange); + } + drawText.append(QStringRef(&text, textStart, textLength)); + } + + layout.setText(drawText); + layout.setAdditionalFormats(ranges); +} +#endif + + +bool QmlStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + if (tag.at(0) == QLatin1Char('b')) { + if (tagLength == 1) { + format.setFontWeight(QFont::Bold); + return true; + } else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + textOut.append(QChar(QChar::LineSeparator)); + return true; + } + } + return false; + } else if (ch->isSpace()) { + // may have params. + QStringRef tag(&textIn, tagStart, tagLength); + if (tag == QLatin1String("font")) + return parseFontAttributes(ch, textIn, format); + if (*ch == greaterThan || ch->isNull()) + continue; + } else if (*ch != slash){ + tagLength++; + } + ++ch; + } + + return false; +} + +bool QmlStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + if (tag.at(0) == QLatin1Char('b')) { + if (tagLength == 1) + return true; + else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) + return true; + } else if (tag == QLatin1String("font")) { + return true; + } + return false; + } else if (!ch->isSpace()){ + tagLength++; + } + ++ch; + } + + return false; +} + +bool QmlStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) +{ + bool valid = false; + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("color")) { + valid = true; + format.setForeground(QColor(attr.second.toString())); + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + return valid; +} + +QPair<QStringRef,QStringRef> QmlStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int attrStart = ch - textIn.constData(); + int attrLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + break; + } else if (*ch == equals) { + ++ch; + if (*ch != singleQuote && *ch != doubleQuote) { + while (*ch != greaterThan && !ch->isNull()) + ++ch; + break; + } + ++ch; + if (!attrLength) + break; + QStringRef attr(&textIn, attrStart, attrLength); + QStringRef val = parseValue(ch, textIn); + if (!val.isEmpty()) + return QPair<QStringRef,QStringRef>(attr,val); + break; + } else { + ++attrLength; + } + ++ch; + } + + return QPair<QStringRef,QStringRef>(); +} + +QStringRef QmlStyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn) +{ + int valStart = ch - textIn.constData(); + int valLength = 0; + while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) { + ++valLength; + ++ch; + } + if (ch->isNull()) + return QStringRef(); + ++ch; // skip quote + + return QStringRef(&textIn, valStart, valLength); +} diff --git a/src/declarative/util/qmlstyledtext_p.h b/src/declarative/util/qmlstyledtext_p.h new file mode 100644 index 0000000..b89b9d9 --- /dev/null +++ b/src/declarative/util/qmlstyledtext_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLSTYLEDTEXT_H +#define QMLSTYLEDTEXT_H + +#include <QSizeF> + +class QPainter; +class QPointF; +class QString; +class QmlStyledTextPrivate; +class QmlStyledText +{ +public: + static void parse(const QString &string, QTextLayout &layout); + +private: + QmlStyledText(const QString &string, QTextLayout &layout); + ~QmlStyledText(); + + QmlStyledTextPrivate *d; +}; + +#endif diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index b3418c7..b6a5c90 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -28,7 +28,8 @@ SOURCES += \ $$PWD/qmlnumberformatter.cpp \ $$PWD/qmldatetimeformatter.cpp \ $$PWD/qmlbehavior.cpp \ - $$PWD/qmlfontloader.cpp + $$PWD/qmlfontloader.cpp \ + $$PWD/qmlstyledtext.cpp HEADERS += \ $$PWD/qmlview.h \ @@ -61,7 +62,8 @@ HEADERS += \ $$PWD/qmlnumberformatter_p.h \ $$PWD/qmldatetimeformatter_p.h \ $$PWD/qmlbehavior_p.h \ - $$PWD/qmlfontloader_p.h + $$PWD/qmlfontloader_p.h \ + $$PWD/qmlstyledtext_p.h contains(QT_CONFIG, xmlpatterns) { QT+=xmlpatterns |