Skip to content

Commit

Permalink
Add linkColor property to Text.
Browse files Browse the repository at this point in the history
Allows the color of links in text to be changed from the default blue.

This currently only works with StyledText and the distance field
rendererer.  It could be made to work with RichText overwriting the
specified foreground color in all instances or by not setting a default
color in the html parser.  The former would prevent the color being
set with CSS or some future means for altering text formats.  The latter
would break rendering with QPainter.

Task-number: QTBUG-23048
Change-Id: I98df215cabe8a089f648fd4a6206622b4318fb8f
Reviewed-by: Martin Jones <martin.jones@nokia.com>
  • Loading branch information
Andrew den Exter authored and Qt by Nokia committed Feb 9, 2012
1 parent a4b4932 commit 8550ed6
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 19 deletions.
6 changes: 3 additions & 3 deletions examples/declarative/twitter/TwitterCore/FatDelegate.qml
Expand Up @@ -92,11 +92,11 @@ Component {

}
Text { id:txt; y:4; x: 56
text: '<html><style type="text/css">a:link {color:"#aaccaa"}; a:visited {color:"#336633"}</style>'
text: '<html>'
+ '<a href="app://@'+userName(name)+'"><b>'+userName(name) + "</b></a> from " +source
+ "<br /><b>" + statusText + "</b></html>";
textFormat: Qt.RichText
color: "#cccccc"; style: Text.Raised; styleColor: "black"; wrapMode: Text.WordWrap
textFormat: Text.StyledText
color: "#cccccc"; style: Text.Raised; styleColor: "black"; wrapMode: Text.WordWrap; linkColor: "#aaccaa"
anchors.left: image.right; anchors.right: blackRect.right; anchors.leftMargin: 6; anchors.rightMargin: 6
onLinkActivated: wrapper.handleLink(link)
}
Expand Down
37 changes: 32 additions & 5 deletions src/quick/items/qquicktext.cpp
Expand Up @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);

QQuickTextPrivate::QQuickTextPrivate()
: color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
: color((QRgb)0), linkColor((QRgb)255), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone),
format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1),
lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
Expand Down Expand Up @@ -1279,6 +1279,34 @@ void QQuickText::setColor(const QColor &color)
}
emit colorChanged(d->color);
}

/*!
\qmlproperty color QtQuick2::Text::linkColor
The color of links in the text.
This property works with the StyledText \l textFormat, but not with RichText.
Link color in RichText can be specified by including CSS style tags in the
text.
*/

QColor QQuickText::linkColor() const
{
Q_D(const QQuickText);
return d->linkColor;
}

void QQuickText::setLinkColor(const QColor &color)
{
Q_D(QQuickText);
if (d->linkColor == color)
return;

d->linkColor = color;
update();
emit linkColorChanged();
}

/*!
\qmlproperty enumeration QtQuick2::Text::style
Expand Down Expand Up @@ -1894,12 +1922,11 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data

if (d->richText) {
d->ensureDoc();
node->addTextDocument(bounds.topLeft(), d->doc, d->color, d->style, d->styleColor);

node->addTextDocument(bounds.topLeft(), d->doc, d->color, d->style, d->styleColor, d->linkColor);
} else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor, d->linkColor);
if (d->elideLayout)
node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, d->color, d->style, d->styleColor);
node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, d->color, d->style, d->styleColor, d->linkColor);
}

foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
Expand Down
5 changes: 5 additions & 0 deletions src/quick/items/qquicktext_p.h
Expand Up @@ -68,6 +68,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QColor linkColor READ linkColor WRITE setLinkColor NOTIFY linkColorChanged)
Q_PROPERTY(TextStyle style READ style WRITE setStyle NOTIFY styleChanged)
Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor NOTIFY styleColorChanged)
Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged)
Expand Down Expand Up @@ -136,6 +137,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
QColor color() const;
void setColor(const QColor &c);

QColor linkColor() const;
void setLinkColor(const QColor &color);

TextStyle style() const;
void setStyle(TextStyle style);

Expand Down Expand Up @@ -201,6 +205,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
void linkActivated(const QString &link);
void fontChanged(const QFont &font);
void colorChanged(const QColor &color);
void linkColorChanged();
void styleChanged(TextStyle style);
void styleColorChanged(const QColor &color);
void horizontalAlignmentChanged(HAlignment alignment);
Expand Down
1 change: 1 addition & 0 deletions src/quick/items/qquicktext_p_p.h
Expand Up @@ -89,6 +89,7 @@ class Q_AUTOTEST_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
QFont font;
QFont sourceFont;
QColor color;
QColor linkColor;
QQuickText::TextStyle style;
QColor styleColor;
QString activeLink;
Expand Down
2 changes: 1 addition & 1 deletion src/quick/items/qquicktextinput.cpp
Expand Up @@ -1752,7 +1752,7 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData

if (!d->m_textLayout.text().isEmpty() || !d->m_textLayout.preeditAreaText().isEmpty()) {
node->addTextLayout(offset, &d->m_textLayout, d->color,
QQuickText::Normal, QColor(),
QQuickText::Normal, QColor(), QColor(),
d->selectionColor, d->selectedTextColor,
d->selectionStart(),
d->selectionEnd() - 1); // selectionEnd() returns first char after
Expand Down
35 changes: 27 additions & 8 deletions src/quick/items/qquicktextnode.cpp
Expand Up @@ -237,6 +237,7 @@ namespace {
static void insert(QVarLengthArray<BinaryTreeNode> *binaryTree,
const QGlyphRun &glyphRun,
SelectionState selectionState,
QQuickTextNode::Decorations decorations,
const QColor &textColor,
const QColor &backgroundColor,
const QPointF &position)
Expand All @@ -247,7 +248,6 @@ namespace {
if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height()))
return;

QQuickTextNode::Decorations decorations = QQuickTextNode::NoDecoration;
decorations |= (glyphRun.underline() ? QQuickTextNode::Underline : QQuickTextNode::NoDecoration);
decorations |= (glyphRun.overline() ? QQuickTextNode::Overline : QQuickTextNode::NoDecoration);
decorations |= (glyphRun.strikeOut() ? QQuickTextNode::StrikeOut : QQuickTextNode::NoDecoration);
Expand Down Expand Up @@ -369,6 +369,11 @@ namespace {
m_textColor = textColor;
}

void setAnchorColor(const QColor &anchorColor)
{
m_anchorColor = anchorColor;
}

void setPosition(const QPointF &position)
{
m_position = position;
Expand Down Expand Up @@ -400,6 +405,7 @@ namespace {
QColor m_textColor;
QColor m_backgroundColor;
QColor m_selectedTextColor;
QColor m_anchorColor;
QPointF m_position;

QTextLine m_currentLine;
Expand Down Expand Up @@ -745,14 +751,14 @@ namespace {
void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun)
{
BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected,
m_textColor, m_backgroundColor, m_position);
QQuickTextNode::NoDecoration, m_textColor, m_backgroundColor, m_position);
}

void SelectionEngine::addSelectedGlyphs(const QGlyphRun &glyphRun)
{
int currentSize = m_currentLineTree.size();
BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Selected,
m_textColor, m_backgroundColor, m_position);
QQuickTextNode::NoDecoration, m_textColor, m_backgroundColor, m_position);
m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize;
}

Expand All @@ -771,11 +777,12 @@ namespace {
addGlyphsInRange(currentPosition, range.start - currentPosition,
QColor(), QColor(), selectionStart, selectionEnd);
}

int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength);
QColor rangeColor = range.format.hasProperty(QTextFormat::ForegroundBrush)
? range.format.foreground().color()
: QColor();
QColor rangeColor;
if (range.format.hasProperty(QTextFormat::ForegroundBrush))
rangeColor = range.format.foreground().color();
else if (range.format.isAnchor())
rangeColor = m_anchorColor;
QColor rangeBackgroundColor = range.format.hasProperty(QTextFormat::BackgroundBrush)
? range.format.background().color()
: QColor();
Expand Down Expand Up @@ -1038,7 +1045,8 @@ void QQuickTextNode::mergeFormats(QTextLayout *textLayout,
for (int i=0; i<additionalFormats.size(); ++i) {
QTextLayout::FormatRange additionalFormat = additionalFormats.at(i);
if (additionalFormat.format.hasProperty(QTextFormat::ForegroundBrush)
|| additionalFormat.format.hasProperty(QTextFormat::BackgroundBrush)) {
|| additionalFormat.format.hasProperty(QTextFormat::BackgroundBrush)
|| additionalFormat.format.isAnchor()) {
// Merge overlapping formats
if (!mergedFormats->isEmpty()) {
QTextLayout::FormatRange *lastFormat = mergedFormats->data() + mergedFormats->size() - 1;
Expand Down Expand Up @@ -1107,13 +1115,15 @@ void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument,
const QColor &textColor,
QQuickText::TextStyle style, const QColor &styleColor,
const QColor &anchorColor,
const QColor &selectionColor, const QColor &selectedTextColor,
int selectionStart, int selectionEnd)
{
SelectionEngine engine;
engine.setTextColor(textColor);
engine.setSelectedTextColor(selectedTextColor);
engine.setSelectionColor(selectionColor);
engine.setAnchorColor(anchorColor);

QList<QTextFrame *> frames;
frames.append(textDocument->rootFrame());
Expand Down Expand Up @@ -1244,6 +1254,13 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex
}
textPos += text.length();
} else {
if (charFormat.foreground().style() != Qt::NoBrush)
engine.setTextColor(charFormat.foreground().color());
else if (charFormat.isAnchor())
engine.setTextColor(anchorColor);
else
engine.setTextColor(textColor);

int fragmentEnd = textPos + fragment.length();
if (preeditPosition >= 0
&& preeditPosition >= textPos
Expand All @@ -1269,13 +1286,15 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex

void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
QQuickText::TextStyle style, const QColor &styleColor,
const QColor &anchorColor,
const QColor &selectionColor, const QColor &selectedTextColor,
int selectionStart, int selectionEnd)
{
SelectionEngine engine;
engine.setTextColor(color);
engine.setSelectedTextColor(selectedTextColor);
engine.setSelectionColor(selectionColor);
engine.setAnchorColor(anchorColor);
engine.setPosition(position);

int preeditLength = textLayout->preeditAreaText().length();
Expand Down
2 changes: 2 additions & 0 deletions src/quick/items/qquicktextnode_p.h
Expand Up @@ -82,10 +82,12 @@ class QQuickTextNode : public QSGTransformNode
void deleteContent();
void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(),
QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
const QColor &anchorColor = QColor(),
const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
int selectionStart = -1, int selectionEnd = -1);
void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(),
QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
const QColor &anchorColor = QColor(),
const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
int selectionStart = -1, int selectionEnd = -1);

Expand Down
1 change: 0 additions & 1 deletion src/quick/util/qdeclarativestyledtext.cpp
Expand Up @@ -625,7 +625,6 @@ bool QDeclarativeStyledTextPrivate::parseAnchorAttributes(const QChar *&ch, cons
format.setAnchorHref(attr.second.toString());
format.setAnchor(true);
format.setFontUnderline(true);
format.setForeground(QColor("blue"));
valid = true;
}
} while (!ch->isNull() && !attr.first.isEmpty());
Expand Down
87 changes: 86 additions & 1 deletion tests/auto/qtquick2/qquicktext/tst_qquicktext.cpp
Expand Up @@ -904,6 +904,7 @@ void tst_qquicktext::color()

QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
QCOMPARE(textObject->styleColor(), QColor());
QCOMPARE(textObject->linkColor(), QColor("blue"));

delete textObject;
}
Expand All @@ -918,6 +919,22 @@ void tst_qquicktext::color()
QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
// default color to black?
QCOMPARE(textObject->color(), QColor("black"));
QCOMPARE(textObject->linkColor(), QColor("blue"));

delete textObject;
}

for (int i = 0; i < colorStrings.size(); i++)
{
QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
QDeclarativeComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());

QCOMPARE(textObject->styleColor(), QColor());
// default color to black?
QCOMPARE(textObject->color(), QColor("black"));
QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(i)));

delete textObject;
}
Expand All @@ -926,13 +943,18 @@ void tst_qquicktext::color()
{
for (int j = 0; j < colorStrings.size(); j++)
{
QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; styleColor: \"" + colorStrings.at(j) + "\"; text: \"Hello World\" }";
QString componentStr = "import QtQuick 2.0\nText { "
"color: \"" + colorStrings.at(i) + "\"; "
"styleColor: \"" + colorStrings.at(j) + "\"; "
"linkColor: \"" + colorStrings.at(j) + "\"; "
"text: \"Hello World\" }";
QDeclarativeComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());

QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(j)));

delete textObject;
}
Expand All @@ -950,6 +972,69 @@ void tst_qquicktext::color()
QCOMPARE(textObject->color(), testColor);

delete textObject;
} {
QString colorStr = "#001234";
QColor testColor(colorStr);

QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
QDeclarativeComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText*>(object.data());

QSignalSpy spy(textObject, SIGNAL(colorChanged(QColor)));

QCOMPARE(textObject->color(), testColor);
textObject->setColor(testColor);
QCOMPARE(textObject->color(), testColor);
QCOMPARE(spy.count(), 0);

testColor = QColor("black");
textObject->setColor(testColor);
QCOMPARE(textObject->color(), testColor);
QCOMPARE(spy.count(), 1);
} {
QString colorStr = "#001234";
QColor testColor(colorStr);

QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStr + "\"; text: \"Hello World\" }";
QDeclarativeComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText*>(object.data());

QSignalSpy spy(textObject, SIGNAL(styleColorChanged(QColor)));

QCOMPARE(textObject->styleColor(), testColor);
textObject->setStyleColor(testColor);
QCOMPARE(textObject->styleColor(), testColor);
QCOMPARE(spy.count(), 0);

testColor = QColor("black");
textObject->setStyleColor(testColor);
QCOMPARE(textObject->styleColor(), testColor);
QCOMPARE(spy.count(), 1);
} {
QString colorStr = "#001234";
QColor testColor(colorStr);

QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStr + "\"; text: \"Hello World\" }";
QDeclarativeComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText*>(object.data());

QSignalSpy spy(textObject, SIGNAL(linkColorChanged()));

QCOMPARE(textObject->linkColor(), testColor);
textObject->setLinkColor(testColor);
QCOMPARE(textObject->linkColor(), testColor);
QCOMPARE(spy.count(), 0);

testColor = QColor("black");
textObject->setLinkColor(testColor);
QCOMPARE(textObject->linkColor(), testColor);
QCOMPARE(spy.count(), 1);
}
}

Expand Down

0 comments on commit 8550ed6

Please sign in to comment.